# Flow — Manage Users & the Dynamic RBAC Matrix (Operation Head)

> Persona: **Anjali Desai** (OH), full access across ALL Game Zones. Source: `PROJECT_PLAN §3` (access matrix), `§9 #4` (Trainee TL = TL), `FOUNDATION_SPEC §1/§2` (dynamic RBAC, `RolePermission`, runtime resolver). Screens: `OH-USERS` → `OH-USER-NEW`, and `OH-ROLES`. Modals: `modals/assign-store-manager.html`, `modals/reassign-manager.html`, `modals/deactivate-user.html`.

---

## Part A — Onboard a user (the auto-assign gate)

### Preconditions
- Anjali is on `OH-USERS` (cross-GZ list). At least one Game Zone exists with rides defined.

### Steps
1. **Open the wizard.** Anjali clicks **+ Onboard User** → `OH-USER-NEW` (a 5-step wizard, `DESIGN_SYSTEM §6.17`): **details → role → reports-to → Game Zone → certified rides**.
2. **Details + role.** She enters name/email/phone + temp password, picks a **role** (any role — OH has full access; SM is limited to ≤ SM in the SM pack).
3. **Game Zone + reports-to.** She picks the **Game Zone**, then the **reports-to picker** (`GET /managers?roleId=&gameZoneId=`) is filtered to valid managers per the **§3 hierarchy** within that GZ (a MON reports to a TL; a TL to an SM; an SM to the OH). Picking an invalid manager is impossible.
4. **Certified rides.** She multiselects the user's **certified rides** from that GZ's ride list — this is the **auto-assign gate** (§9 #2): a person only gets ride-safety checklists for rides they are rostered on **AND** certified for.
5. **Finish.** → `POST /users` (`{ roleId, gameZoneId, reportsToId, certifiedRideIds }`) → back to `OH-USERS` with a success toast. The new user's `/me` will resolve nav + permissions from the matrix on their first login.

### Edit / reassign / deactivate (`OH-USER-EDIT`)
- **Reassign manager:** changing reports-to opens the **reassign-manager confirm modal** (old → new + sub-tree impact) → `PATCH /users/:id`.
- **Deactivate:** opens the **deactivate modal** (warns about open assignments) → `isActive=false`.

---

## Part B — Edit the dynamic RBAC matrix (the "fully open RBAC" posture)

### Preconditions
- Anjali is on `OH-ROLES` — the **editable** 7-role access matrix backed by `RolePermission` (`FOUNDATION_SPEC §1`). This is the **single source of truth** the runtime resolver reads on every request (L4).

### Steps
1. **Read the grid.** Rows = permissions (`module.action`: `checklist.approve`, `roster.create`, `user.manage`, …); columns = roles (OH/SM/TL/SCM/MON/CS). Each cell = an **allowed toggle** + a **scope select** (`all | own | own_team`). A note states **Trainee TL = TL rights** (§9 #4) — no separate column.
   → `GET /roles/matrix`.
2. **Change a cell.** Say Anjali wants Senior Court Monitors to be able to verify (normally they can't). She toggles `checklist.approve` ON for `SCM` and sets scope `own_team`.
   → `PATCH /roles/SCM/permissions` (`{ changes:[{ permissionId, allowed:true, scope:"own_team" }] }`) — writes the `RolePermission` row with `updatedBy` audit.
3. **It takes effect immediately.** The runtime resolver reads `RolePermission` **per request** — the very next SCM request reflects the new permission. **No redeploy, no restart** (`FOUNDATION_SPEC §2`). A scope change shows a confirm first.
4. **Revert if needed.** A **Reset to seed** affordance (`POST /roles/matrix/reset`) restores the `PROJECT_PLAN §3` starting matrix.

### Why this matters (the locked posture)
- The "fully open RBAC" decision means **no endpoint hardcodes a role check** — `OH-ROLES` is what makes that real. Every gate in every pack resolves against this table at runtime.
- Because it is dynamic, the access matrix in `PROJECT_PLAN §3` is a **starting seed, not a contract** — Anjali can change it live without an engineering change.

## Result
- A correctly onboarded user (right role, right reporting line, right Game Zone, right certified rides → correct auto-assigned checklists), and an RBAC matrix the OH can re-tune live, effective on the next request.
