Commit Graph

2 Commits

Author SHA1 Message Date
d1ae8f943f feat(account): self-service account deletion, MODULE_API 1.20.0
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 37s
PR Checks / client-build (pull_request) Successful in 49s
PR Checks / server-tests (pull_request) Successful in 6m14s
ACCOUNT_DELETION.md phase 2 (core): a member deletes their own account on
the website, and the admin route runs the same procedure.

- DELETE /api/v1/auth/me/account: typed username, password always (AD7),
  and a 2FA or recovery code when 2FA is on. A wrong password or code is
  400, not 401 (AD12), so the app's refresh-on-401 never replays it.
  409 password_required and last_admin; the demo's shared logins are
  locked.
- auth/accountDeletion.js: module hooks first, then one transaction that
  reserves a staff username (reserved_usernames, AD9) and deletes the row,
  then a Teams reconcile, the audit entry (no username) and a notice to
  the old address (new protected template auth.account-deleted).
- MODULE_API 1.20.0: api.onUserDeleting({ userId, deletedBy }), before the
  delete, 10 s limit, cannot veto (AD10, AD11).
- utils/displayAuthor.js: a deleted account's username snapshots read
  "[deleted player]" to members and the stored name, marked, to staff
  (AD5, AD6). Used by the forum, the Reports queue, the guest list and
  the Team digest. A deleted account drops off Forum guests (AD13).
- Reserved names refuse at every username write, answered as "taken".
- Web: Delete my account on the player and staff Account pages; staff
  views mark "(deleted account)".
- OpenAPI regenerated; route manifest and engagement manifest regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-29 18:32:19 -05:00
128de0ff2e test(teams): phase 5's server surface, and the negative property under it
1008 pass (972 before). The tests worth reading first are the ones that pin a
property no screen would look different without:

  * **The edit window is decided on the server, twice.** One test proves the read
    path stamps `canEdit` per post per viewer; another proves the WRITE path
    re-derives it from `created_at` and refuses a stale edit even though the
    client was told it could — because a time-bounded permission must not take its
    clock from the party it bounds.

  * **A locked thread refuses staff too**, asserted over member, leader and staff
    in one loop, at 409 rather than 403: well-formed request, refusing state.

  * **delete → restore is reversible for images.** Without the second half of the
    pair a restored post returns its words and loses its pictures a retention
    window later, silently — the test asserts both calls and that `hide` makes
    neither.

  * **Post moderation recomputes the thread's counters** rather than nudging them;
    the test runs hide → unhide → hide, which is the cycle a delta gets wrong.

  * **acceptance: nothing in the report model is reachable by a Team leader.** The
    negative property is the whole point of §5.6 and negatives are what nobody
    notices going, so it is asserted directly — the module's function surface is
    pinned, and `queue`/`handle` are checked not to mention leadership at all. If
    a leader-facing queue is ever wanted it is the org lead's decision, and this
    test is what makes somebody ask.

  * **A report never changes the content it is about**, proved by stubbing every
    mutation the forum has to throw. If filing a report touched a status then
    "report" would BE moderation, and the first person to work that out would have
    found a way to hide anything on the site.

The test suite caught one real defect: `describeTarget` returned `undefined` for a
hard-deleted target, and `undefined` is dropped by JSON.stringify — so the
documented `target: null` would have reached clients as an absent key.

Two phase-4 tests were updated rather than added to, both because phase 5 changed
what they describe: `canPost` split into `canPost` (open a discussion, everyone)
and `canAnnounce` (leaders), and `discussion` is no longer a refused thread type.
Phase 5's four new player routes are added to acceptance criterion 2's list, so
"with the forum off every forum route 404s" keeps covering the whole surface.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 12:58:15 -05:00