FAILURE MAP
← Case archive

FA-75971 / Chat ordering and read receipts / Open access

Filter read receipts by privacy settings and chat kind: direct reciprocity · case 01

A viewer who disabled their own receipts can still see other people's receipts in direct chats.

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

ROOT CAUSE

The direct reciprocity decision evaluates `ok = enabled(u)` where the contract requires `ok = enabled(u) and enabled(viewer)`.

VERIFIED REPAIR

Use `ok = enabled(u) and enabled(viewer)` for the direct reciprocity decision and keep every other rule of the model unchanged.

Unsuccessful approach: Requiring either flag still shows receipts to a viewer who opted out. The attempted `ok = enabled(u) or enabled(viewer)` still disagrees with a fixture.

Case contract

settings[u] is a user's "send read receipts" flag (default True). In direct chats a reader is shown only when both the reader and the viewer have receipts enabled (reciprocity). In group chats only the reader's flag matters. In channels only admins see receipts and they see every reader regardless of settings. The viewer never sees their own receipt. Result: sorted visible readers.

Why this case matters

Receipt privacy rules differ by conversation type; mistakes leak receipts users disabled or hide ones they may see.

1 / The failure

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

N = 1
observations = []
def solve(viewer, readers, settings, chat_kind, admins):
    enabled = lambda u: settings.get(u, True)
    out = []
    for u in readers:
        if u == viewer:
            continue
        if chat_kind == 'channel':
            ok = viewer in admins
        elif chat_kind == 'direct':
            ok = enabled(u)
        else:
            ok = enabled(u)
        if ok:
            out.append(u)
    return sorted(out)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('missing settings default on', ('v', ['r1'], {}, 'direct', []), ['r1']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r1'], {}, 'group', []), ['r1']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 2: [('missing settings default on', ('v', ['r2'], {}, 'direct', []), ['r2']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r2'], {}, 'group', []), ['r2']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 3: [('missing settings default on', ('v', ['r3'], {}, 'direct', []), ['r3']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r3'], {}, 'group', []), ['r3']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 4: [('missing settings default on', ('v', ['r4'], {}, 'direct', []), ['r4']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r4'], {}, 'group', []), ['r4']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 5: [('missing settings default on', ('v', ['r5'], {}, 'direct', []), ['r5']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r5'], {}, 'group', []), ['r5']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
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
missing settings default on['r1']['r1']Passed
viewer disabled in direct chat['r'][]Failed
viewer disabled in group['r']['r']Passed
reader disabled in direct chat[][]Passed
channel admin['p', 'q', 'r']['p', 'q', 'r']Passed
channel non-admin[][]Passed
own receipt in group['r1']['r1']Passed
own receipt in channel as admin[][]Passed

SHA-256 / e710790410bdb9a48d492a5d20ab5c79036d1523b9f2a0a40b74be5bd3947015

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(viewer, readers, settings, chat_kind, admins):
    enabled = lambda u: settings.get(u, True)
    out = []
    for u in readers:
        if u == viewer:
            continue
        if chat_kind == 'channel':
            ok = viewer in admins
        elif chat_kind == 'direct':
            ok = enabled(u) or enabled(viewer)
        else:
            ok = enabled(u)
        if ok:
            out.append(u)
    return sorted(out)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('missing settings default on', ('v', ['r1'], {}, 'direct', []), ['r1']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r1'], {}, 'group', []), ['r1']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 2: [('missing settings default on', ('v', ['r2'], {}, 'direct', []), ['r2']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r2'], {}, 'group', []), ['r2']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 3: [('missing settings default on', ('v', ['r3'], {}, 'direct', []), ['r3']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r3'], {}, 'group', []), ['r3']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 4: [('missing settings default on', ('v', ['r4'], {}, 'direct', []), ['r4']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r4'], {}, 'group', []), ['r4']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 5: [('missing settings default on', ('v', ['r5'], {}, 'direct', []), ['r5']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r5'], {}, 'group', []), ['r5']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
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
missing settings default on['r1']['r1']Passed
viewer disabled in direct chat['r'][]Failed
viewer disabled in group['r']['r']Passed
reader disabled in direct chat['r'][]Failed
channel admin['p', 'q', 'r']['p', 'q', 'r']Passed
channel non-admin[][]Passed
own receipt in group['r1']['r1']Passed
own receipt in channel as admin[][]Passed

SHA-256 / 3d62e47f0e8b82691ee687423fe4aac896cb3afe686d64b5012381dc8657b908

3 / The verified repair

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

N = 1
observations = []
def solve(viewer, readers, settings, chat_kind, admins):
    enabled = lambda u: settings.get(u, True)
    out = []
    for u in readers:
        if u == viewer:
            continue
        if chat_kind == 'channel':
            ok = viewer in admins
        elif chat_kind == 'direct':
            ok = enabled(u) and enabled(viewer)
        else:
            ok = enabled(u)
        if ok:
            out.append(u)
    return sorted(out)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('missing settings default on', ('v', ['r1'], {}, 'direct', []), ['r1']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r1'], {}, 'group', []), ['r1']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 2: [('missing settings default on', ('v', ['r2'], {}, 'direct', []), ['r2']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r2'], {}, 'group', []), ['r2']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 3: [('missing settings default on', ('v', ['r3'], {}, 'direct', []), ['r3']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r3'], {}, 'group', []), ['r3']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 4: [('missing settings default on', ('v', ['r4'], {}, 'direct', []), ['r4']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r4'], {}, 'group', []), ['r4']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])], 5: [('missing settings default on', ('v', ['r5'], {}, 'direct', []), ['r5']), ('viewer disabled in direct chat', ('v', ['r'], {'v': False}, 'direct', []), []), ('viewer disabled in group', ('v', ['r', 'q'], {'v': False, 'q': False}, 'group', []), ['r']), ('reader disabled in direct chat', ('v', ['r'], {'r': False, 'v': True}, 'direct', []), []), ('channel admin', ('v', ['r', 'q', 'p'], {'r': False}, 'channel', ['v']), ['p', 'q', 'r']), ('channel non-admin', ('v', ['r'], {}, 'channel', ['a']), []), ('own receipt in group', ('v', ['v', 'r5'], {}, 'group', []), ['r5']), ('own receipt in channel as admin', ('v', ['v'], {}, 'channel', ['v']), [])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
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
missing settings default on['r1']['r1']Passed
viewer disabled in direct chat[][]Passed
viewer disabled in group['r']['r']Passed
reader disabled in direct chat[][]Passed
channel admin['p', 'q', 'r']['p', 'q', 'r']Passed
channel non-admin[][]Passed
own receipt in group['r1']['r1']Passed
own receipt in channel as admin[][]Passed

SHA-256 / cc76d3fff826f13ffbe45e088089ff8ba069ab5926a314a4b0cb1ca2919a9d60

Verification & scope

Stipulated offline chat model; not a complete messaging protocol, client or server implementation. 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:49:11.979422+00:00.

Case digest / 2b8e347392ee4c1c13a6d236f1e395e587f5d86d0b180819a44b325c51324547