The email/name/external-id search was scoped to one tab and only looked in the live
identities table, so a deleted account (whose credentials moved to retained_identities on
deletion) could not be found by the email/id it held. Now a people search spans live and
deleted accounts in one query (robots only on the Robots tab) and also matches the
retention journal and the retained real name; results carry a 'deleted' badge. Integration
test: a deleted account is found by its held email and external id.
- Admin /users gains a Deleted scope (between People and Robots); every other scope now
hides tombstoned accounts (deleted_at filter in UserFilter).
- Admin delete-user is offered for any non-deleted account (drop the not-guest gate, so a
stale is_guest account can still be tombstoned).
- Harden EmailService.ConfirmCode to ClearGuest — defence-in-depth so no confirmed-email
path leaves is_guest set (the currently-live paths already do).
- Space the delete/change dialog's action row from its input field.
Integration tests: the Deleted filter scoping + ConfirmCode guest promotion.
The user-detail console gains a Deletion & retention panel: last login (time + IP),
the tombstone (deleted-at + retained real name), and the retention journal (the legal
dossier of detached credentials). A Delete-user action runs the same deletion
orchestration as the in-app flow (mirrors the email-erase pattern). Store readers
RetainedIdentities + DeletionInfo back the view; integration test covers them.
Step-up: email accounts confirm with a mailed code (purpose=delete, no deeplink —
ConfirmByToken refuses a delete token so a stray click can't delete); platform-only
accounts type a fixed phrase (anti-impulse). Endpoints /user/delete/{request,confirm};
the confirm orchestration resigns active games, drops all-robot games, tombstones +
anonymizes the account (freeing its creds), and revokes its sessions — the tombstone is
the point of no return, the rest best-effort. Gateway account.delete.{request,confirm}
ops + fbs AccountDeleteConfirm/AccountDeleteRequestResult + branded ru/en delete email.
Integration tests cover the step-up (code + no-email) and the orchestration pieces.
Q2=B: DropAllRobotGames deletes the deletee's games with no human opponent (vs-AI or
auto-match-robot; children cascade), keeping games with any human seat (anonymized
instead). Q1=A: the anon label is the sentinel [Deleted] — the editable-name rule forbids
brackets, so no live player can impersonate a deleted account. Integration test covers
drop-vs-keep.
New accountdelete package: AnonymizeAndTombstone journals every credential to the
retention log (reason=delete) then removes them (freeing email/vk/tg for reuse),
snapshots the real display name into deleted_display_name and scrubs the live one to
'Удалённый игрок', sets deleted_at, anonymizes the account's game-seat snapshots, and
drops its friendships/blocks/invitations/friend-codes/drafts/pending-codes — all in one
transaction. Chat, feedback and complaints are kept (the tombstone keeps their
no-cascade FKs valid). Session revocation + game forfeit are orchestrated a layer up.
Integration test covers journalling, tombstone/scrub and credential reuse.