fix(privacy): stop erasure scrubbing a member who shares a name (#407) #475
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/407-erasure-ambiguity"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #407.
What was left
Most of the transcript half was already fixed. An earlier pass rebuilt transcript scrubbing around
transcript_segments.track_owner_id(#335), which settles attribution exactly for anything recorded since. That is untouched here. The issue's cited evidence had decayed accordingly — it describeslabelsdriving everything, which is no longer true for rows-backed sessions.What the owner key cannot reach was still live:
seen_labelsseeded straight from the raw display/character names — #407's exact scenario, regardless of rows.The fix
_ambiguous_labelsfinds the names a departing member shares with somebody still in the campaign (display names and every character they ran, compared case- and whitespace-insensitively, the way a person would). Those names are withheld from every name-based decision. The authoritative owner key is untouched and still erases exactly.Under-erasing is the right way to fail here. Withholding leaves a gap someone can close by hand; deleting the wrong person's quote cannot be undone. The audit context now carries
ambiguous_labelsandquotes_withheld, and both log warnings — so an incomplete erasure says so rather than looking like one that finished.The WAV question from the earlier comment
The comment on this issue flagged that
erase_member_recordingsunlinks audio immediately with no grace, and that combined with mis-targeting that is unrecoverable.It turned out not to be at risk. The filename is keyed on the verified
PlatformLinkDiscord id, never a name, so it always deleted exactly the requesting member's track. The comment's preferred option — "if the target is unambiguous the deletion is safe" — is satisfied because that path was never ambiguous. Recorded in the commit so the question is not reopened from scratch.Two things the mutation check caught in my own work
Worth flagging because both would have shipped looking fine:
My regression test for the headline scenario was vacuous. It used an
[MM:SS]timestamp where_TRANSCRIPT_LINE_RErequires[HH:MM:SS], so the scrub matched nothing and the test passed whether or not the guard existed. Fixed, with the format noted inline.I had filtered ambiguous labels in two places five lines apart — seeding
seen_labelsfrom the safe set and subtracting at the delete. Each hid the other: removing either left every test green. Collapsed to a single filter at the point of deletion, which a test now pins, including the path where the ambiguous label re-enters via the owner rows rather than the seed.Verification
12 new tests. 7 mutations, all caught: disabling the check entirely, feeding ambiguous names to either the transcript scrub or the quote delete, recording nothing in the audit trail, case/whitespace-sensitive comparison, comparing a member against themselves, and ignoring character names.
Full backend suite 1558 passed (including the 12 pre-existing erasure tests, untouched);
ruff checkandruff format --checkclean.🤖 Generated with Claude Code