Feature Owner (original implementation): leidc024 (Leian Carl Dela Cruz) — original RBAC + Manage Users UI (2025-07)
Updated by / current owner: scorevi (multi-role system, admin user routes, majority of fixes)
Hand-Off author: Patrick Babala
Module: User Management — [6.1] Manage Users (with [6.3] Suspend User and [6.4] Permanent User Delete)
Priority: P0
Status: Handoff — New (v1.0)
Date: 2026-10-01
Audit: docs/FEATURE_AUDIT_MAY2026.md Module 6 — User Management
QA cases: QA-073.5 (Secure User Delete), QA-081 (Verification Prompt Shows)
Companion doc: Internal Technical Guide — Internal-Technical-Guide-Manage-Users.md (architecture, DB schema, API specs, helpers, ops, troubleshooting). This Hand-Off stays product-facing; where engineering depth is needed it cross-references the ITG instead of duplicating it.
EXECUTIVE SUMMARY
What is this feature?
Manage Users is the Super-Admin console at /admin/manage-users for controlling platform access. It lists every user with their platform roles and agency-assigned role, lets an admin assign one or many roles, invite new users by email, suspend or unsuspend access, and permanently delete an account. Destructive actions (role change, delete, suspend) are gated behind a re-authentication challenge and written to an audit trail.
Why does it matter?
Roles decide what every user can see and do across WyzQuests. Before this console, access decisions were manual and unaudited. Manage Users centralizes role assignment, onboarding, suspension, and removal into a single screen, and makes every sensitive action attributable through audit_logs.
MVP scope (current implementation):
List all users with platform roles, primary role, and agency-assigned role.
Assign multiple platform roles per user (normalized
user_roles).Invite a user by email: create a Supabase Auth account, assign roles, send a branded “set your password” email.
Suspend / unsuspend a user (
app_users.status).Permanently delete a user (typed confirmation + re-authentication).
Sync profile name/email/avatar from Supabase Auth into the app profile.
Re-authentication challenge (≤5 min, single-use) for destructive actions.
Audit logging for role changes, deletes, invites, lookups, and re-auth events.
Shipped scope (v1.0):
Database —
user_roles(multi-role junction),admin_reauth_tokens,audit_logs;app_users.role,.status,.profile_image_url.API —
list-users,change-role,delete-user,invite-user,suspend-user,unsuspend-user,reauth(GET/POST),sync-users,lookup-user.UI — Manage Users table,
MultiSelectrole editor,ReAuthModal, sharedConfirmDestructiveModal,InviteUserDialog,SuspendDialog,UnsuspendDialog.
Still deferred: soft-delete / GDPR erasure with a grace window, bulk actions, an in-app audit-log viewer, session revocation on role change, resend-invite, and per-user activity timeline.
1. USER PAIN POINT & SOLUTION
Current State (Without Feature)
There is no single console for access control. Role changes and account removal require manual database work, invitations are done outside the product, suspension is not consistently enforced, and there is no attributable record of who changed what.
Pain Point
Emotional: Admins are anxious about irreversible actions and cannot confirm a change actually took effect.
Functional: No multi-role support, no clean onboarding path, and no audit trail for sensitive actions.
Business Impact: Slower user onboarding, inconsistent access control, and no compliance record for deletions or privilege changes.
Future State (With Feature)
An admin opens one screen, sees every user and their roles, and safely assigns roles, invites, suspends, or deletes accounts. Destructive actions require the admin to re-verify their identity, and every action is logged.
Marketing Hook
“Every role, invite, and access decision in one audited console.”
2. CODEBASE ASSESSMENT
Current Implementation Status
Manage Users is implemented across four database objects, nine API routes, one admin page, and a set of dialogs. All endpoints are ADMIN-only; role change, delete, and suspend additionally require a re-auth token; delete also requires a typed confirmation.
Primary Files
app/admin/manage-users/page.tsx— the console: user table, sorting, role MultiSelect, actions menu, dialog orchestration.app/api/admin/list-users/route.ts— lists users; also serves agency owner/member pickers viamode.app/api/admin/change-role/route.ts— multi-role diff and apply (re-auth gated).app/api/admin/invite-user/route.ts— create auth user, assign roles, send email, roll back on failure.app/api/admin/suspend-user/route.ts/unsuspend-user/route.ts— status flip + auth cache invalidation.app/api/admin/delete-user/route.ts— typed-confirm + re-auth delete.app/api/admin/reauth/route.ts— issues/consumes the ≤5-minute single-use token.app/api/admin/sync-users/route.ts— profile sync from Supabase Auth.app/api/admin/lookup-user/route.ts— email lookup + agency eligibility (audited).components/admin/manage-users/{InviteUserDialog,SuspendDialog,UnsuspendDialog}.tsx,components/admin/ReAuthModal.tsx,components/shared/confirm-destructive-modal.tsx,components/ui/MultiSelect.tsx.supabase/migrations/20260612_create_user_roles_multi_role.sql,20260526_create_admin_reauth_tokens.sql,20260414_create_audit_logs_table.sql,20260430_add_profile_image_url_to_app_users.sql,20260219_add_reviewer_role.sql.
Current Behavior Summary
The console is reachable only to ADMIN (
app/admin/layout.tsx,constants/getNavGroups.ts).Users can hold multiple platform roles; the highest-priority role is mirrored to
app_users.rolefor backward compatibility.Agency membership roles are shown read-only alongside platform roles.
Invites create a Supabase Auth account and email a password-reset link (no credentials are ever emailed).
Suspension is enforced on page navigation by middleware and inside the API auth chain, not only in this console.
Every sensitive action is written to
audit_logs.
Strengths Already Present
Multi-role model with a clear priority order and an agency-role bridge.
Re-auth tokens are single-use and time-bounded; failed verification is audited.
Invite flow is self-healing: any failure after account creation rolls back the auth user, profile, and roles.
Delete requires both a re-auth token and a typed
DELETEtoken re-validated server-side.Suspension takes effect immediately in the serving process and is asserted on cache-hit branches.
Gaps / Hardening Opportunities
Deletes are hard (no
deleted_at/ grace window).delete-userhas no compensating rollback; a failed final delete can leave an orphaned auth account.The multi-step role change is not transactional.
Expired re-auth tokens have no cleanup job (the migration comment assumes a nightly job).
app_usersis defined outsidesupabase/migrations/, so a fresh database cannot be rebuilt from the repo.No unit tests for the admin routes; e2e specs are brittle and hardcode credentials.
Button copy still says “Sync from Clerk” after the Supabase Auth migration.
unsuspend-useris not re-auth gated whilesuspend-useris.Invite email rate limiting exists on
origin/developbut is absent in the current checkout (see Open Questions).
3. 4D FRAMEWORK MAPPING
Diagnose
Admins lacked a safe, auditable way to assign roles, onboard, suspend, and remove users.
Design
Model access as a set of platform roles per user (user_roles) plus a lifecycle status (app_users.status), and gate every destructive mutation behind re-authentication and an audit record.
Develop
Provide ADMIN-only APIs for list/role/invite/suspend/delete/sync/lookup, a re-auth token service, and a console that surfaces roles, status, and actions.
Deliver
Admins manage the entire user lifecycle from one screen with confidence that actions are deliberate and recorded.
4. USER FLOWS
Entry Point
Admin signs in → /admin → sidebar Manage Users → /admin/manage-users. (The page is guarded server-side by an ADMIN role check; non-admins are redirected or shown Unauthorized.)
Success Criteria
Every user appears with correct platform roles, agency role (if any), and status.
A role change succeeds only after successful re-authentication and is audited.
An invited user receives the invitation email and can set a password.
A suspended user loses access; an unsuspended user regains it.
A deleted user’s account, profile, and memberships are removed; the deletion is audited.
A profile sync updates names/emails without touching roles.
Main Flow (Happy Path)
Admin opens Manage Users; the table loads all users (
GET /api/admin/list-users).Admin changes a user’s roles in the Change Role multi-select;
ReAuthModalopens.Admin verifies identity (password, or email confirmation for Google accounts).
POST /api/admin/change-roleconsumes the token, diffs roles, saves, and returns success.Admin clicks Invite User, enters an email and roles;
POST /api/admin/invite-usercreates the account and sends the email.Admin suspends a user: Actions → Suspend → verify identity →
POST /api/admin/suspend-user.Admin deletes a user: Actions → Delete → type
DELETE→ verify identity →DELETE /api/admin/delete-user.Admin optionally clicks Sync from Clerk to refresh names/emails (
POST /api/admin/sync-users).
Edge Cases
Wrong re-auth password: action is blocked;
REAUTH_FAILEDis audited; modal stays open for retry.Expired/used token:
403— “Re-authentication token is invalid or expired…”.Delete an ADMIN: blocked with “Cannot delete admin users”.
Delete a missing user:
404— “User not found”.Invite an existing email:
409with guidance to use Change Role.Invite email fails: the invite is rolled back so it can be retried cleanly.
Not an admin / View-As mode: re-auth returns
403; the modal explains to exit View-As.Admin’s own account suspended: API returns
403 ACCOUNT_SUSPENDED.No roles selected: the Change Role multi-select refuses to submit an empty set.
Decision Points
IF the caller is not ADMIN → no access to the console or APIs.
IF the user has an agency-assigned role → that role is disabled (read-only) in the multi-select.
IF multiple roles are selected → the highest-priority becomes the primary
app_users.role.IF the target is the last page of the users table → no pagination issues exist today (full list returned).
5. INFORMATION ARCHITECTURE
Primary Information (Always visible)
User (avatar, name, short internal ID)
Email
Platform role badges
Change Role multi-select
Status (
Active/Suspended)Actions menu
Secondary Information
Agency-assigned role badge (
role· agency name, read-only)Invite User button
Sync from Clerk button (profile refresh)
Tertiary Information (Hidden until needed)
Full internal
app_users.idclerk_id(Supabase auth UUID)Audit records (
audit_logs)
Actions
Primary CTA: Invite User
Secondary Actions: Change Role, Sync from Clerk, Suspend / Unsuspend, Delete User (destructive, confirmation + re-auth required)
6. WIREFRAMES
The feature is a single Super-Admin page: a card with a header (title, description, Invite User and Sync from Clerk) and a sortable user table.
Key Screens:
Manage Users table (sortable columns: User, Email, Role, Change Role, Status, Actions)
Invite User dialog (email + role multi-select)
Confirm Identity modal (
ReAuthModal: password or email confirmation)Delete User modal (
ConfirmDestructiveModal: typeDELETE)Suspend / Unsuspend confirmation dialogs
Annotations:
Agency-assigned roles render as read-only badges and are disabled in the multi-select.
An empty role selection is rejected client-side (at least one role required).
Status drives which action appears (Suspend for Active, Unsuspend for Suspended).
The Sync from Clerk label is legacy copy; the sync now reads Supabase Auth.
The architecture/system diagram lives with the companion ITG (
Internal-Technical-Guide-Manage-Users.md), not here.
7. WIREFLOWS
Admin opens Manage Users → table loads → selects roles in Change Role → Confirm Identity (password/email) → roles saved and toast shown → optionally Invite User (email + roles) → invitation email sent → optionally Suspend/Unsuspend (identity check for suspend) → optionally Delete (typed DELETE then identity check) → account and memberships removed and audited → optionally Sync from Clerk to refresh profile fields.
8. PROTOTYPE
Figma Prototype Link: Not currently available for this feature.
How to test:
Sign in as an ADMIN and open
/admin/manage-users.Change a user’s roles in the Change Role column and complete the Confirm Identity prompt.
Click Invite User, enter an email and roles, and confirm the invite email is received.
Suspend a user, then verify they lose access; unsuspend and verify access returns.
Delete a test user: type
DELETE, complete the identity prompt, and verify the row disappears and anaudit_logsUSER_DELETEentry exists.
9. DATA MODEL
Core Tables
app_users— the application profile row:id(internal UUID),clerk_id(auth UUID, legacy name),email,name,role(primary role for backward compatibility),status(Active/Suspended),profile_image_url,agency_id. This table is created outsidesupabase/migrations/and is not reconstructable from the repo.user_roles— one row per user↔platform-role pair (user_idFK →app_users.idON DELETE CASCADE,role∈ ADMIN/AGENCY/CREATOR/REVIEWER/LEARNER,UNIQUE(user_id, role)).admin_reauth_tokens— short-lived single-use tokens (admin_clerk_id,tokenUNIQUE,expires_at,used,ip_address).audit_logs— sensitive-action trail (user_id,action,entity_type,entity_id,detailsJSONB,ip_address,user_agent,created_at).
Primary Role Rule
A user may hold multiple roles. The primary role stored in app_users.role is the highest priority: ADMIN > AGENCY > CREATOR > REVIEWER > LEARNER.
Agency Roles
Roles held within an agency (agency_members.role) are merged into the displayed role set but shown read-only; they cannot be changed from Manage Users.
Full column types, indexes, constraints, RLS policies, and the re-auth token lifecycle are in the ITG §3 (Data Model / Schema).
10. API CONTRACTS
All endpoints require the ADMIN platform role. Re-auth token endpoints enforce ADMIN unconditionally (they bypass the dev/staging View-As bypass).
List Users
Endpoint:
GET /api/admin/list-users(optional?mode=picker|members&agencyId=)Auth: ADMIN.
Behavior: returns all users with
role,status, enrichedroles[], andagencyRole/agencyName;modevariants serve the agency owner/member pickers.Response:
{ users: [...] }.
Change Role
Endpoint:
POST /api/admin/change-roleAuth: ADMIN +
reauthToken.Body:
{ userId, roles: string[], reauthToken }.Behavior: consumes the token, diffs current vs. requested roles, applies deletes/inserts, mirrors the primary role; audits
ROLE_CHANGE.Response:
200— “User role updated successfully”.
Delete User
Endpoint:
DELETE /api/admin/delete-userAuth: ADMIN +
reauthToken+ typedconfirm_text: "DELETE".Behavior: blocks ADMIN targets, deletes the auth account, cleans up agency memberships, deletes the profile row; audits
USER_DELETE.Response:
200— “User <email> has been permanently deleted”.
Invite User
Endpoint:
POST /api/admin/invite-userAuth: ADMIN.
Body:
{ email, roles: string[] }.Behavior: rejects existing emails (409), creates the auth user, syncs the profile, assigns roles, emails a set-password link; rolls back all artifacts on any failure.
Response:
201 { userId, emailSent: true }.
Suspend / Unsuspend User
Endpoint:
POST /api/admin/suspend-user(re-auth gated) /POST /api/admin/unsuspend-userBody:
{ userId }(suspend also{ reauthToken }).Behavior: sets
app_users.statusand invalidates the in-process auth caches; audits the re-auth outcome.Response:
200— “User suspended successfully” / “User unsuspended successfully”.
Re-Authenticate
Endpoints:
GET /api/admin/reauth(returns{ method, hasOAuth }),POST /api/admin/reauthBody:
{ password }(email/password) or{ confirmEmail }(OAuth).Behavior: verifies identity, issues a 5-minute single-use token; audits
REAUTH_SUCCESS/REAUTH_FAILED.Response:
200 { token, expiresInSeconds: 300 }.
Sync Users
Endpoint:
POST /api/admin/sync-users(optional?clerkId=)Behavior: pulls name/email/avatar from Supabase Auth and updates
app_users(batched).Response:
200 { synced, failed }.
Lookup User
Endpoint:
GET /api/admin/lookup-user?email=Behavior: returns profile + agency eligibility; records a
USER_LOOKUPaudit entry (enumeration guard).
Auth checks, exact validation schemas, and full request/response shapes are in the ITG §3 (API Specification).
11. DATA REQUIREMENTS
Frontend Needs
The full user list (id, clerk_id, name, email, primary role, status, all roles, agency role/name).
Whether the current admin can act (ADMIN role, plus successful re-auth for gated actions).
Role option set (
LEARNER,CREATOR,AGENCY,REVIEWER,ADMIN).The auth method for the current admin (password vs. OAuth) to render the correct re-auth input.
Invite and sync results for user feedback.
Backend Needs
app_users(profile + status + primary role).user_roles(multi-role assignments).admin_reauth_tokens(token issue/consume).audit_logs(action trail).Supabase Auth admin API (create/delete/get/list users).
agency_members+agencies(agency-role enrichment).Email service + branding (invitation email).
12. SECURITY & AUTHORIZATION
Who can access this feature?
ADMIN: ✔ full access (list, change role, invite, suspend, unsuspend, delete, sync, lookup).
AGENCY / CREATOR / REVIEWER / LEARNER: ✘ cannot open the console or call the APIs.
Invited user: receives an email only; no console access.
Authorization Logic
Every route calls
authenticateRole("ADMIN"); the admin layout re-checks ADMIN server-side before rendering.Role change, delete, and suspend require a re-auth token that is single-use, belongs to the requesting admin, and has not expired (≤5 minutes).
Re-auth deliberately enforces ADMIN even in dev/View-As mode.
Delete additionally re-validates the typed
DELETEtoken server-side.The identity provider is Supabase Auth; the legacy-named
app_users.clerk_idcolumn stores the Supabase auth UUID. Paths and columns named “clerk” are legacy names.Suspension is enforced on page navigation (middleware) and inside the API auth chain, independent of this console.
All sensitive actions are written to
audit_logs(ROLE_CHANGE,USER_DELETE,INVITE_USER,USER_LOOKUP,REAUTH_SUCCESS,REAUTH_FAILED,REAUTH_TOKEN_INVALID).
Note: re-auth tokens are stored server-side only; the token table is not client-accessible by policy, and the runtime uses the service-role client.
13. ERROR HANDLING
Common Errors
Invalid/expired re-auth token (“Re-authentication token is invalid or expired. Please re-authenticate.”).
Wrong password (“Invalid password. Re-authentication failed.”).
Verification service unavailable (“Could not reach the verification service. Please try again in a moment.”).
OAuth email mismatch (“Email does not match your account.”).
Non-admin session (“Admin role required for re-authentication” / “Your account does not have admin privileges…”).
Target not found (“User not found”).
Deleting an admin (“Cannot delete admin users”).
Existing email on invite (“A user with this email already exists. Use Change Role to update their roles.”).
Invite email failure (“The invitation email could not be sent, so the invite was rolled back. Please try again.”).
Suspended admin account (
403 ACCOUNT_SUSPENDED).Validation errors (“userId must be a valid UUID”, “reauthToken must be a valid UUID”, “At least one role is required”).
Handling Guidance
On a re-auth
403, re-open the Confirm Identity modal and retry; do not retry the destructive call without a new token.On
409invite, switch the user to Change Role instead of re-inviting.On an invite email failure, retry the invite (it was fully rolled back).
Treat
403 ACCOUNT_SUSPENDEDon the acting admin as an out-of-band suspension; contact a platform owner.Surface exact messages to the admin; do not silently swallow role or delete failures.
14. TESTING CHECKLIST
Happy Path
Admin opens
/admin/manage-users; all users load with roles and status.Change role (single and multiple) with a valid re-auth token succeeds and updates the table.
Invite a new email; the invitation email arrives; the user appears in the list.
Suspend a user; status flips to Suspended; unsuspend restores Active.
Delete a user after typing
DELETEand verifying identity; the row disappears.Sync from Clerk refreshes names/emails without changing roles.
Edge Cases
Wrong re-auth password → action blocked, modal stays open,
REAUTH_FAILEDaudited.Reused/expired token →
403on the action.Delete an ADMIN →
403“Cannot delete admin users”.Delete a non-existent user →
404.Invite an existing email →
409.Invite email fails → invite rolled back, retry succeeds.
Empty role selection → cannot submit.
Agency-assigned role → shown read-only and disabled in the multi-select.
Non-admin / View-As session → re-auth
403with guidance.Suspended acting admin →
403 ACCOUNT_SUSPENDED.
Coverage Notes
Automated unit coverage exists only for the shared suspension layer (
tests/api/suspension-enforcement.test.ts).E2E coverage:
tests/e2e/admin/secure-user-delete.spec.ts(QA-073.5) andtests/e2e/admin/role-change-verification-prompt.spec.ts(QA-081); both hardcode credentials and use brittle selectors.tests/e2e/suspension/suspension.spec.tscovers suspension behavior.No route-level unit tests exist for role change, invite, sync, or lookup.
15. OPEN QUESTIONS
For Product
Should user deletion be a soft delete with a grace window and purge, for GDPR / right-to-erasure? — Still open. Today it is a hard delete.
Should unsuspend also require re-authentication? — Still open. Suspend does; unsuspend does not.
Should changing a user’s roles revoke their active sessions? — Still open. No session revocation today; role changes apply on the next authorization check.
Should there be a Resend Invite action for an invite that was received but not acted on? — Still open. A new invite cannot be created for an existing account (409).
Should the console include an in-app audit-log viewer? — Still open. Audit data is stored but shown only in the DB.
For Engineering
Should
app_usersbe captured insupabase/migrations/so environments are reproducible? — Still open (currently out-of-band).Is there a nightly cleanup job for expired
admin_reauth_tokensas the migration assumes? — Still open / needs ops input.Should
lookup-usereligibility use the fulluser_rolesset instead of only the primary role? — Still open.Should the admin routes get route-level unit tests and a stable Playwright spec? — Still open.
Is invite email rate limiting (present on
origin/develop) merged into this branch? — Still open for this checkout.
16. OUT OF SCOPE (v1.1+)
Soft-delete / GDPR purge workflow with a grace window — out of scope.
Bulk role assignment, bulk suspend, bulk delete — out of scope.
Session revocation on role change — out of scope.
CSV import / export of users — out of scope.
Per-user activity timeline or content reassignment on delete — out of scope.
Managing agency membership from Manage Users — out of scope (owned by Manage Agencies).
Self-service profile editing — out of scope (owned by user settings).
Renaming the legacy
clerk_id/ “Clerk” copy — out of scope (deferred cleanup).
17. SUCCESS METRICS
Role-change success rate (post-re-auth).
Invite delivery success rate.
Suspend/unsuspend success rate.
Delete completion rate with audit trail present.
Re-auth failure rate (wrong password vs. infra) as a security signal.
Time to onboard a new user (invite → first sign-in).
Count of
audit_logsentries per sensitive action (coverage).
18. DEPENDENCIES
This feature depends on
Authentication (Supabase Auth) — session resolution, admin create/delete/get/list.
app_usersprofile table (out-of-band schema).user_rolesmulti-role model.admin_reauth_tokens+ the re-auth service.audit_logsstore.agency_members+agencies(agency-role enrichment and owner/member pickers).Email service + branding (invitation email).
Shared UI:
MultiSelect,ReAuthModal,ConfirmDestructiveModal.Admin layout role guard.
These features depend on this
Every role-gated area (Creator, Agency, Reviewer, Learner dashboards) — roles set here decide access.
Suspension enforcement across the API auth chain and middleware.
Manage Agencies add-member/owner pickers (
list-users?mode=picker|members,lookup-user).Enrollment / team flows that resolve user identity and roles.
Audit and compliance reporting.
19. TIMELINE & OWNERSHIP
Original owner / Hand-Off author: leidc024 (Leian Carl Dela Cruz) — original RBAC and Manage Users UI (2025-07).
Lead / current owner: scorevi — multi-role system, admin routes, majority of fixes (2026-02 → 2026-06).
Contributors: Joylynne Esportuno (suspend/unsuspend + rollback), Onise Cortez (secure delete with admin password + audit logging), Patrick Babala (email invite,
ConfirmDestructiveModal, Supabase Auth Phase 2/3), Christian Denzon (suspension relocation + uniform 403, PR #840), clydetims (invite email rate limiting onorigin/develop), Mich-Tapawan (theme/restructuring), WyzQuests Deploy (multi-role UI fixes).Reviewers: Patrick Babala, Christian Denzon (“cdnzn”) — PR #840 review rounds.
QA: Manual end-to-end verification plus QA-073.5 and QA-081 Playwright specs; shared suspension unit tests.
Shipped: incremental; consolidated multi-role work on 2026-06-12, Supabase auth cutover on 2026-09-10, suspension relocation merged via PR #840 (2026-09-21).
Estimated Completion: core complete; remaining items are the Open Questions and Out of Scope sections above.
REFERENCES
Audit:
docs/FEATURE_AUDIT_MAY2026.md— Module 6, User Management ([6.1]Manage Users,[6.3]Suspend User,[6.4]Permanent User Delete).Primary PRs: #840 — suspension relocation / uniform
403 ACCOUNT_SUSPENDED; #777 — sharedConfirmDestructiveModal.Related PRs: #406 — View-As role switching; #520, #558, #563, #607 — release syncs.
Migrations:
supabase/migrations/20260219_add_reviewer_role.sql,20260414_create_audit_logs_table.sql,20260430_add_profile_image_url_to_app_users.sql,20260526_create_admin_reauth_tokens.sql,20260612_create_user_roles_multi_role.sql,20260612_update_rls_policies_multi_role.sql,20260909_create_auth_users_triggers.sql.QA:
tests/e2e/admin/secure-user-delete.spec.ts(QA-073.5),tests/e2e/admin/role-change-verification-prompt.spec.ts(QA-081),tests/api/suspension-enforcement.test.ts,tests/e2e/suspension/suspension.spec.ts.Companion ITG:
Internal-Technical-Guide-Manage-Users.md.
VERSION HISTORY
1.0 — 2026-10-01 — Patrick Babala, Initial Hand-Off for Manage Users (
[6.1], with[6.3]/[6.4]). Covers the user console, multi-role assignment, email invites, suspend/unsuspend, permanent delete, re-authentication, audit logging, API contracts, testing checklist, and known limitations.