# Verixa — User Requirements Specification

# Module 2: Role-Based Access Control & Permissions

| Field | Value |
|---|---|
| Document ID | VRX-URS-02 |
| Version | 1.0 |
| Status | Final — ready for QA, Validation, Regulatory Affairs, Information Security, and Founder approval. URS approval is separate from validation execution. This document becomes "Approved Controlled URS — released for engineering implementation and validation planning" only after signature capture in the Document Approval block. It becomes "Released for validation execution" only after the module migration evidence gate (URS-02-VAL-008) and validation evidence pack are satisfied. |
| Document Type | User Requirements Specification (URS) |
| GAMP 5 Category | Category 5 — Custom Application |
| Regulatory Classification | Critical infrastructure — operates the role catalogue, permission matrix, and access-governance lifecycle that gate every regulated record across the Verixa platform. |
| Date of Issue | 2026-05-05 |
| Module Owner (Engineering) | Platform / Identity Squad |
| Module Owner (Quality Validation) | CSV / CSA Lead — Access Governance |
| Module Owner (Compliance) | Quality Assurance, Regulatory Affairs |
| Approving Authority | Founder / Chairman & MD; QA Head; Validation Head; RA Head; Information Security Head |

## Document Approval

| Role | Name | Signature | Date |
|---|---|---|---|
| Author — Platform Architecture | _____________________ | _____________________ | __________ |
| Reviewer — Engineering Lead | _____________________ | _____________________ | __________ |
| Reviewer — QA / Validation Lead | _____________________ | _____________________ | __________ |
| Reviewer — Information Security Lead | _____________________ | _____________________ | __________ |
| Reviewer — Regulatory Affairs Lead | _____________________ | _____________________ | __________ |
| Approving Authority — Founder, Chairman & MD | _____________________ | _____________________ | __________ |

## Version History

| Version | Date | Summary |
|---|---|---|
| 1.0 | 2026-05-05 | First issued user requirements specification for Module 2. |

---

## 0. Document Framing

### 0.1 Purpose of this document

This URS defines the target expected state for Verixa's Role-Based Access Control and Permissions module (Module 2). It is the binding contract between product, engineering, quality validation, regulatory affairs, information security, and the executive authority for the design, implementation, validation, release, and on-going periodic review of the role catalogue, permission matrix, access-governance lifecycle (invite, role change, deactivate, reactivate, remove, force password reset), the effective-permissions endpoint, and the front-end three-layer guard hierarchy that this module owns. Compliance with this URS is mandatory.

### 0.2 Audience

- Engineering — design, implementation, code review.
- Quality Assurance / Validation — IQ / OQ / PQ test design, traceability matrix, validation reports.
- Regulatory Affairs — regulatory mapping, inspection readiness.
- Information Security — security review, penetration testing, incident response.
- Product Owners — feature definition, UX acceptance.
- Inspectors and Auditors (FDA, EU GMP / MHRA, CDSCO, ISO, customer audits, internal audits) — evidence of system intent and controls.
- Customers — confirmation of platform behaviour during qualification of Verixa as a supplier.

### 0.3 Reading conventions

- Requirements use the keywords MUST, SHOULD, and MAY as defined in IETF RFC 2119 / RFC 8174.
- Atomic requirement identifier form: `URS-02-<Domain>-<Number>`. Domain prefixes: `FE` (front-end), `BE` (back-end), `WF` (workflow), `DATA` (data model), `SEC` (security), `AUD` (audit), `ESIG` (electronic signature), `AI` (AI / HITL), `INT` (integration), `REP` (reports / exports), `VAL` (validation evidence).
- Supporting identifiers: `DEC-02-N` (closed launch decision — the closed launch decision identifier), `DEP-02-N` (dependency), `BR-02-N` (business rule), `RG-02-N` (regulatory mapping row), `TC-PLAIN-N` (plain-language scenario), `TC-TECH-N` (technical test case), `AC-02-N` (Given/When/Then acceptance criterion), `CIM-N` (change-impact matrix row).
- If this document lacks evidence for a true external dependency, record it under Dependencies or Validation Evidence Gates. Internal design decisions are closed in this URS. Invention is forbidden.

### 0.4 Inherited cross-module standards

The twelve standards defined in URS-01 §0.4 apply unchanged to Module 2: three-layer guard hierarchy, Controlled Approval Modal contract, API path discipline, tenant isolation, audit trail with rollback on audit-write failure, ALCOA+ universal compliance, fail-secure on resolver / matrix failure, claims-version mismatch handling, no optimistic updates on regulated actions, AI is suggestion-only, Document Quality Gateway interception, encryption discipline.

Module-specific additions:

13. **Mode A admin invariant.** The base role `admin` has unconditional full access within its tenant; the permission matrix MUST NOT weaken `admin`. Any attempt to mutate an `admin`-role row in the matrix MUST be rejected with `ADMIN_IMMUTABLE` and audited.
14. **Self-modification block.** A user MUST NOT be permitted to mutate their own role, their own membership status, or their own permission matrix entries; the back end rejects with `SELF_ROLE_MODIFICATION_FORBIDDEN` and audits.
15. **No ad-hoc role checks.** Backend route handlers MUST decide authorisation through the permission matrix or through documented role guards in `hooks/rbac.ts`; inline string comparisons such as `if (request.user.role !== 'admin')` are prohibited outside the documented exemption list and require an inline justification comment.
16. **Effective-permissions atomicity.** `GET /access/me/permissions` MUST return `baseRole`, `effectivePermissions`, `tenantId`, `matrixVersion`, and `claimsVersion` in a single response so that the front end can correlate access decisions with the matrix version that made them.

### 0.5 Verixa URS suite — module map

This module is **URS-02** in the Verixa launch URS suite of thirty-five modules. Cross-module references use the module number and short title:

| Module | Title |
|---|---|
| URS-01 | Authentication, Session Management & Access Control |
| URS-02 | Role-Based Access Control & Permissions *(this document)* |
| URS-03 | Context Gate & Approval Scope |
| URS-04 | Workflow / Human-in-the-Loop / Electronic Signature / Approval Authority |
| URS-05 | Authority Profile / Approval Delegation / Segregation of Duties |
| URS-06 | Audit Trail & Data Integrity |
| URS-07 | Study Management |
| URS-08 | Tenant Management |
| URS-09 | Sites Management |
| URS-10 | Products Management |
| URS-11 | Supplier Management |
| URS-12 | Document Control & Knowledge Libraries |
| URS-13 | Regulated change governance |
| URS-14 | Complaints |
| URS-15 | Out-of-Specification / Out-of-Trend |
| URS-16 | Deviations |
| URS-17 | Root Cause Analysis |
| URS-18 | Corrective and Preventive Actions |
| URS-19 | Risk Assessment |
| URS-20 | Reviews |
| URS-21 | Findings |
| URS-22 | Inspection Management & Readiness |
| URS-23 | Batch Records & Manufacturing Execution |
| URS-24 | Stability Management |
| URS-25 | Environmental Monitoring |
| URS-26 | Annual Product Quality Review |
| URS-27 | Regulatory Intelligence & Submission Governance |
| URS-28 | Training Management & Qualification |
| URS-29 | Screen Reader / Data Capture & Extraction Governance |
| URS-30 | Notifications, Delivery Channels & Communication Governance |
| URS-31 | Document Quality Gateway |
| URS-32 | MIRA AI Chat / Command Center / Agent Orchestration |
| URS-33 | Good Manufacturing Practice — Manufacturing Operations & Controls |
| URS-34 | Good Distribution Practice — Distribution, Warehouse & Dispatch Controls |
| URS-35 | Infrastructure, Backup, Restore & Operational Resilience |

### 0.6 Glossary

#### Roles, permissions, and matrix

- **Role-Based Access Control (RBAC)** — access-control model in which permissions are assigned to roles and users acquire permissions through role assignment.
- **Base role** — the coarse access tier persisted on the user-tenant `memberships` row. Verixa operates exactly five base roles: `admin`, `quality_lead`, `reviewer`, `auditor`, `viewer`.
- **Role catalogue** — the controlled registry of base roles with key, label, description, hierarchy order, mutability, and intended use.
- **Permission matrix** — the tenant-scoped table of `(tenant_id, role, resource, action, is_allowed)` tuples that defines which role may perform which action on which resource within the tenant.
- **Resource** — a named area of the application gated by the matrix (for example `deviations`, `capas`, `documents`, `users`, `permissions`, `audit`).
- **Action** — a named verb gated by the matrix (for example `read`, `create`, `update`, `delete`, `export`, `manage`, `configure`, `approve_config`, `read_sensitive`, `manage_users`, `manage_roles`, `classify`, `escalate`, `override`, `purge`, `restore`, `review`, `write`, `read_executive`, `read_operational`, `read_tactical`).
- **Effective permission** — the union of role-derived and tenant-policy-derived permissions returned to the user at session bootstrap and on demand. Always tenant-scoped.
- **Matrix version** — a monotonic counter incremented on every accepted matrix mutation; returned in the bootstrap response so the front end can detect a stale matrix and refresh.
- **Mode A admin invariant** — the rule that the `admin` row in the permission matrix is immutable and cannot be weakened by configuration; the matrix UI rejects edits to `admin`; the back end rejects `admin`-row mutations with `ADMIN_IMMUTABLE`.
- **Self-modification block** — the rule that a user cannot mutate their own role, membership, or permissions through any user-invoked path; rejected with `SELF_ROLE_MODIFICATION_FORBIDDEN`.

#### Access governance lifecycle

- **Invitation** — the controlled flow that creates a user record in `pending` state, issues a single-use invite token, sends an e-mail, and admits the user only after they accept and authenticate.
- **Role change** — the controlled flow that mutates `memberships.role` and triggers session revocation, claims-version increment, and an audit row attributing the change.
- **Deactivation** — the controlled flow that sets `users.status = 'inactive'` and `memberships.is_active = false`, revokes all sessions, and prohibits subsequent login until reactivation.
- **Reactivation** — the controlled flow that re-enables a deactivated user; requires electronic signature and reason; resumes membership in the tenant.
- **Removal** — the controlled flow that hard-deletes a membership when permitted by the platform fallback policy, otherwise falls back to deactivation; emits `USER_REMOVAL_FALLBACK_TO_DEACTIVATION` if the fallback applies.
- **Force password reset** — the controlled flow that sets `users.force_password_change = true` and revokes all sessions; the user must change password before any further regulated action.
- **Access-governance report** — the auditor-grade tenant report listing role assignments, role-change events, deactivations, reactivations, removals, force-password-reset events, and matrix changes within a date range.
- **Administrative provisioning** — the controlled flow by which a `platform_admin` or `super_admin` directly creates a user (rather than inviting them); reserved for migration and supplier onboarding scenarios; emits `ADMINISTRATIVE_PROVISIONING`.

#### Engineering and platform

- **PermissionGuard** — front-end component that gates a section of the user interface by `(resource, action)`; authoritative gate for buttons and panels.
- **RoleGuard** — front-end component that gates a route or section by base role; coarse and used only for module-entry decisions.
- **AuthorityGuard** — front-end component owned by URS-01 that gates regulated-decision buttons by Authority Profile; nested inside `PermissionGuard`.
- **/access/me/permissions** — the bootstrap endpoint for effective permissions; returns `{baseRole, effectivePermissions, tenantId, matrixVersion, claimsVersion}`.
- **Permission action taxonomy registry** — the controlled list of permission verbs; updates require URS-13 because consumers gate on these names.
- **Permission resource taxonomy registry** — the controlled list of permission nouns; updates likewise gated by URS-13.

#### Regulatory and process

- **21 CFR Part 11 §11.10(d)** — limits on system access to authorised individuals; the principal regulatory anchor for this module.
- **21 CFR Part 11 §11.300** — controls over identification codes and passwords; consumed where Module 2 changes membership status or forces password reset.
- **EU GMP Annex 11 §12** — logical access controls; consumed by every assignment, role change, and matrix mutation.
- **EU GMP Annex 11 §11** — periodic review of computerised systems; consumed by access-review obligations.
- **ALCOA+** — the data-integrity principles applied to every governance event in Module 2.

If a reader is uncertain about a term not in this glossary, the term goes into an Dependencies / Validation Evidence Gates; unclear language is treated as a defect in this URS.

---

## 1. Module Purpose

**Plain-language primer (read before §1 if you are a non-domain engineer or a product owner).** Role-Based Access Control (RBAC) decides whether a user can access a module or an action category at all (for example: enter the deviations module, see the matrix editor, click "Export"). An Authority Profile decides whether the user is permitted to make a regulated decision (for example: approve a CAPA closure, release a batch, sign off on a deviation). An electronic signature records who approved a regulated action, when, and why. **All three are required for a regulated administrative change.** RBAC alone is necessary but not sufficient. Authority alone is necessary but not sufficient. Electronic signature alone is necessary but not sufficient. Module 2 owns RBAC; URS-05 owns Authority Profiles; URS-04 owns the electronic-signature ceremony. The three-layer model is shown in Diagram 0.4-A.

#### Diagram 0.4-A — Three-layer authorisation for a regulated administrative change

```mermaid
flowchart LR
  A([Authenticated identity]) --> B[Base role on memberships<br/>admin / quality_lead / reviewer / auditor / viewer]
  B --> C{Permission matrix<br/>resource × action × is_allowed<br/>for tenant?}
  C -- denied --> X1[403 PERMISSION_DENIED<br/>or RBAC_DENIED forensic row]
  C -- allowed --> D{Authority Profile in scope?<br/>tenant_admin_authority for Module 2 admin actions}
  D -- denied --> X2[403 AUTHORITY_CHECK_FAILED]
  D -- allowed --> E[Controlled Approval Modal:<br/>password + meaning + reason]
  E --> F{Re-authentication +<br/>multi-factor step-up where high-risk}
  F -- denied --> X3[401 INVALID_CURRENT_PASSWORD]
  F -- allowed --> G[Action committed<br/>+ electronic signature row<br/>+ audit row<br/>+ matrix-version increment where applicable]
```

Module 2 is the role catalogue, permission matrix, and access-governance lifecycle for Verixa. It defines the five base roles, the per-tenant permission matrix that grants or denies actions on resources, the lifecycle that admits, modifies, suspends, removes, and re-enables users in a tenant, and the bootstrap surface that resolves the user's effective permissions at session start.

The module creates regulated records (memberships, permission-matrix rows, role-change events, governance events, access-governance reports) and supports regulated workflows (invitation, role change, deactivation, reactivation, removal, force password reset, matrix mutation, administrative provisioning). It does not authenticate users (URS-01 owns identity) and it does not gate regulated approval decisions (URS-05 Authority Profiles and URS-04 workflow / electronic signature own those). It governs **module access** and **fine-grained permissions within modules**; it never substitutes for Authority Profiles when a regulated decision is being made.

The architectural boundary remains explicit. URS-01 answers "who are you?". This module (URS-02) answers "can you enter this module and perform this action category?". URS-05 answers "are you authorised to sign off this regulated decision?". A regulated approval requires all three layers in sequence and never just one.

---

## 2. Scope

### 2.1 In scope

- Five-role catalogue with mutability flags, hierarchy order, key, label, description, intended use; exposed via a controlled read endpoint.
- Tenant-scoped permission matrix with `(tenant_id, role, resource, action, is_allowed)` tuples, unique-constraint enforcement, idempotent upsert, monotonic matrix version, atomic audit on every mutation, and Mode A admin invariant.
- Effective-permissions resolution: bootstrap endpoint returning user, base role, effective permissions, tenant identifier, matrix version, claims version.
- Centralised resource and action taxonomy registry, version-controlled and validated on every matrix mutation.
- Access-governance lifecycle: invite, accept-invite handoff to URS-01, role change, deactivate, reactivate, remove (with documented fallback to deactivation), force password reset.
- Self-modification block: a user cannot mutate own role, membership, or permission matrix.
- Administrative provisioning by `platform_admin` or `super_admin` only; emits `ADMINISTRATIVE_PROVISIONING` audit event with reason.
- Front-end administration: user list, user detail, invitation form, role-change modal, deactivate / reactivate / remove modals, force-password-reset modal, permission-matrix editor, role-catalogue viewer, access-governance report, own-effective-permissions view.
- Front-end three-layer guard hierarchy: `RoleGuard` (coarse), `PermissionGuard` (RBAC fine-grained), `AuthorityGuard` (regulated; nested inside `PermissionGuard`); URS-02 owns `RoleGuard` and `PermissionGuard`.
- Backend RBAC enforcement hook with deny-by-default, fail-closed semantics; route metadata coverage check; backend re-validation of permission decisions.
- Audit-governance event taxonomy: the canonical launch event vocabulary listed in §6.6, with attribution, before/after state, reason, and electronic signature where required.
- Cross-module wiring: every regulated module consumes Module 2 effective permissions on the back end and `PermissionGuard` on the front end.
- Periodic access review and audit-trail review obligations specific to access-governance events.
- Reports: role-assignment report, governance-events report, matrix-history report, permission-coverage report, dormant-user report.

### 2.2 Out of scope

- Authentication, multi-factor authentication, single sign-on, password policy, lockout, IP allowlist, session lifecycle, refresh-token rotation (owned by URS-01).
- Authority Profiles, delegations, segregation-of-duties rules, scope-rule registry (owned by URS-05).
- Tenant lifecycle (creation, suspension, deletion), tenant configuration, tenant data residency (owned by URS-08).
- Master-data CRUD for sites, products, suppliers, studies (owned by URS-07, URS-09, URS-10, URS-11).
- Workflow templates, electronic-signature ceremonies, human-in-the-loop gates (owned by URS-04 and URS-06).
- AI-driven permission recommendations or auto-assignment of roles (explicitly prohibited at launch).

### 2.3 Closed launch decisions

The table below carries the binding closed decisions on which this URS rests. They are the launch design premises. Changes to any decision are routed through the Change-Impact Matrix in §7.

| Identifier | Closed launch decision |
|---|---|
| DEC-02-01 | The five base roles are fixed and enforced at the database CHECK constraint; the role catalogue is not extensible at the base-role layer. Tenants encode business specialisations through Authority Profiles (URS-05). |
| DEC-02-02 | Mode A admin invariant is the launch policy; admin permissions cannot be weakened from the matrix, and the `admin`-row mutation path is rejected with `ADMIN_IMMUTABLE`. |
| DEC-02-03 | The launch permission action taxonomy is the canonical list declared in §6.1.2 (thirty-six verbs). Adding an action is a Class 3 change. Renaming or removing an action is Class 1. |
| DEC-02-04 | The launch removal policy is "soft-fall-back-to-deactivation" — hard removal is permitted only when no regulated record references the user (per the trigger graph in §6.1.5); otherwise the system deactivates and emits `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`. |
| DEC-02-05 | Direct user creation (administrative provisioning) is restricted to `platform_admin` and `super_admin` identities; an `admin` (or any tenant role) cannot directly create users and must use the invitation flow. |
| DEC-02-06 | A role change always revokes all existing sessions of the affected user, increments the user's claims version, and writes a hash-chained `ROLE_DOWNGRADED` row when the new role is lower in hierarchy than the old. |
| DEC-02-07 | The matrix version is monotonic per tenant and increments atomically on every accepted mutation; idempotent no-op upserts neither write an audit row nor increment the version. |
| DEC-02-08 | The effective-permissions endpoint returns at most one row per `(resource, action)` for the caller's role; the resolver computes the union and returns a flat permission list. |
| DEC-02-09 | Inviter retention: the user identifier of the inviter is persisted with the invitation row and survives invitation expiry for audit traceability. |
| DEC-02-10 | The access-governance event vocabulary is the canonical list declared in §6.6; addition of any new code is a Class 3 change per §7.2. |
| DEC-02-11 | No prohibited inline role checks are permitted at release. All back-end authorisation decisions MUST use the RBAC hook, the permission matrix, or a documented exemption. Static analysis MUST show zero prohibited inline role checks before release. Any remaining inline role check is a release blocker unless listed in the exemption appendix with risk rationale, owner, and test evidence. |
| DEC-02-12 | The role-catalogue field name in the shared registry is `description` for launch; alignment with the spec preference `intendedUse` is a Class 4 cosmetic change scheduled for a subsequent release; downstream consumers MUST read `description` until rename. |

---

## 3. User Roles and Permissions

### 3.1 Architecture

Module 2 governs Layer 1 (base role) and Layer 2 (permission matrix) of the Verixa role universe. Layer 3 (Authority Profiles) is owned by URS-05 and consumed by Module 2 only for administrative regulated actions (matrix edit, role change, deactivate, reactivate, remove, force password reset). Module 2 enforces, on the front end, `RoleGuard` and `PermissionGuard`; `AuthorityGuard` (URS-01 component, URS-05 semantics) is the third-layer gate nested inside `PermissionGuard` for regulated administrative buttons.

### 3.2 Role definitions

The five tenant-level base roles are fixed and enforced at the database level; they are not extensible. Two cross-tenant platform identities operate outside the per-tenant `memberships.role` enum.

| Role | Mutability | Purpose |
|---|---|---|
| `admin` | Immutable in the matrix (Mode A invariant) | Tenant administrator with full access; cannot be weakened from the matrix. |
| `quality_lead` | Mutable | Quality-team operator who edits, submits, reviews, and (with the right Authority Profile) approves regulated records. |
| `reviewer` | Mutable | Comment, review, acknowledge; cannot approve. |
| `auditor` | Mutable | Read-only access including audit-trail reads; suitable for internal and external auditors. |
| `viewer` | Mutable | Read-only access to records the user is in scope for; default for new and just-in-time-provisioned users. |
| `platform_admin` (cross-tenant) | Immutable platform-side | Verixa support staff with cross-tenant visibility for incident response. |
| `super_admin` (cross-tenant) | Immutable platform-side | Verixa platform staff with full administrative authority over the platform. |

### 3.3 Authority Profiles consumed by Module 2

Module 2 consumes Authority Profiles owned by URS-05 for its regulated administrative actions. The launch list of consumed profiles:

| Authority Profile (consumed) | Module 2 action gated |
|---|---|
| `tenant_admin_authority` | invite user, change user role, deactivate user, reactivate user, force password reset, edit permission matrix, export access-governance report |

The role catalogue (`role_catalogue`) is a global / platform-context registry. Tenant administrators may **read** it; they MUST NOT edit it. Editing of `label` and `description` is restricted to `platform_admin` / `super_admin` identities; `key` and `hierarchy_order` are immutable after launch (per §6.3.2 and DEC-02 hierarchy-mutation rule). This separation prevents tenant-side drift in the global role hierarchy on which `ROLE_DOWNGRADED` semantics depend.
| platform-only (no profile; identity check) | direct administrative provisioning of a user (`platform_admin` or `super_admin` identity required) |

Tenant-defined Tier 2 Authority Profiles are not introduced by Module 2; they are introduced by URS-05 and may be consumed in custom URS-04 workflow templates that wrap Module 2 administrative actions.

### 3.4 Segregation-of-Duties rules

| SoD rule (consumed from URS-05) | Module 2 application |
|---|---|
| Self-modification forbidden | A user cannot change own role, deactivate self, reactivate self, remove self, force-password-reset self, or edit own permission-matrix entries; back end rejects with `SELF_ROLE_MODIFICATION_FORBIDDEN`. |
| Inviter ≠ first approver | The inviter MUST NOT be the first reviewer / approver of a regulated record subsequently created by the invited user when an invitation-mediated bootstrap path is used; the SoD engine flags such combinations during workflow assignment. |
| Author ≠ approver of policy change | The user who edits a permission-matrix row cannot be the user who approves the matrix snapshot in the access-governance report electronic signature. |

### 3.5 Worked examples

#### Worked example A — Tenant administrator invites a new employee

| Lifecycle role | Record-level field | Layer 1 base role | Layer 2 Authority Profile | SoD |
|---|---|---|---|---|
| Inviter | `user_invitations.created_by` | `admin` (holding `tenant_admin_authority`) | `tenant_admin_authority` | inviter MUST NOT be later assigned as primary approver of records the invitee opens |
| Invitee (just-in-time provisioned on accept) | `users.id`, `memberships.user_id` | `viewer` (default) — invite payload MAY specify any of the five base roles within the tenant administrator's prerogative | none auto-assigned | — |

**Narrative.** The QA Manager (`admin` holding `tenant_admin_authority`) opens `/admin/users/invite`, enters the invitee's e-mail and the intended base role, gives reason "New QA Reviewer hire — start date 1 September 2026", and signs the invitation through the Controlled Approval Modal. The system writes a `user_invitations` row, sends the invitation e-mail via URS-30, emits `INVITE_SENT` and `ROLE_ASSIGNED_PENDING` audit events. When the invitee follows the link and completes URS-01's accept-invite flow, the system writes the `users` and `memberships` rows, emits `INVITE_ACCEPTED` and `ROLE_ASSIGNED` audit events, and the access-governance report records both ends of the lifecycle.

#### Worked example B — Tenant administrator changes a user's base role

| Lifecycle role | Record-level field | Layer 1 base role | Layer 2 Authority Profile | SoD |
|---|---|---|---|---|
| Role-change actor | `audit_log.actor_user_id` for the `ROLE_ASSIGNED` event | `admin` (holding `tenant_admin_authority`) | `tenant_admin_authority` | actor MUST NOT be the target user (self-modification block) |
| Target user | `memberships.user_id` | the new role | unchanged Authority Profiles | claims-version increment + session revoke + `ROLE_DOWNGRADED` if downgrade |

**Narrative.** Sarah is promoted from `reviewer` to `quality_lead`. The QA Manager opens `/admin/users/<sarah>/role`, selects `quality_lead`, gives reason "Promotion approved per HR-2026-0815", and signs through the Controlled Approval Modal. The system updates `memberships.role`, emits `ROLE_ASSIGNED` with the old and new values in `before_state` / `after_state`, increments Sarah's claims version, revokes her active sessions (forcing fresh login with the new role's matrix), and writes a `ROLE_DOWNGRADED` row to the authority change log only if the change was a downgrade. Sarah's next login resolves a new effective-permissions set against the matrix; the front end rerenders gates accordingly.

#### Worked example C — Tenant administrator edits the permission matrix

The QA Manager wants to grant `quality_lead` the right to export deviation records. They open `/admin/permissions`, locate the row `(role=quality_lead, resource=deviations, action=export)`, toggle from `is_allowed = false` to `is_allowed = true`, give reason "QA Lead requires export for inspection prep — approved per CC-2026-0144", and sign through the Controlled Approval Modal. The system rejects the mutation if it targets the `admin` row (`ADMIN_IMMUTABLE`), validates the resource and action against the taxonomy registry, performs an idempotent upsert with `onConflict.doUpdateSet`, increments the tenant's matrix version, emits `PERMISSION_UPDATED` with old and new `is_allowed`, links the audit row to the electronic signature, and the next bootstrap response carries the new matrix version so existing sessions silently re-resolve effective permissions on next bootstrap poll.

#### Worked example D — Self-modification blocked

The same QA Manager attempts to widen their own administrative permissions to include a non-standard custom action. The back end detects that `actor.user_id` equals `target.user_id` for a self-permission edit and rejects with `SELF_ROLE_MODIFICATION_FORBIDDEN`. The user interface surfaces the inline error in the matrix editor; no row is mutated; an audit event records the rejected attempt.

#### Worked example E — Mode A admin invariant

A `platform_admin` attempts to disable `(role=admin, resource=users, action=manage_users)` for a tenant. The back end rejects with `ADMIN_IMMUTABLE`. The matrix editor disables the `admin` row entirely on the front end so the attempt is normally not reachable; back-end enforcement is the authoritative gate. The reject is audited.

#### Worked example F — Removal falls back to deactivation

The QA Manager attempts to remove a user who has authored a regulated record (a deviation that has not been closed). The system detects the foreign-key reference, rejects hard removal with `USER_HAS_REGULATED_REFERENCES`, and offers to fall back to deactivation. The QA Manager confirms; the system deactivates the user, emits `USER_REMOVAL_FALLBACK_TO_DEACTIVATION` and `USER_DEACTIVATED`, revokes all sessions, and the user is now `inactive` in the tenant rather than removed. The historical link from the deviation to the user identifier remains intact for audit traceability.

### 3.6 Role-permission matrix (Module 2 administrative surface only)

The matrix below covers Module 2's own administrative surface. Every row that performs a regulated decision is base role plus the matching Authority Profile. The full per-tenant permission matrix that Module 2 maintains for every other module is the runtime data of this module, not part of this URS.

| Action (within Module 2) | viewer | reviewer | quality_lead | auditor | admin | platform_admin | super_admin | system identity | Authority Profile |
|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|---|
| Read own effective permissions | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — |
| Read role catalogue | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — |
| Read tenant permission matrix | — | — | — | ✓ (read-only) | ✓ | support / break-glass only | support / break-glass only | — | — |
| Read user list (own tenant) | — | — | — | ✓ | ✓ | support / break-glass only | support / break-glass only | — | — |
| Read user detail | — | — | — | ✓ | ✓ | support / break-glass only | support / break-glass only | — | — |
| Invite user | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Edit permission matrix (non-`admin` rows) | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Mutate `admin` matrix row | — | — | — | — | — | — | — | — | rejected by `ADMIN_IMMUTABLE` invariant |
| Change another user's role | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Deactivate another user | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Reactivate another user | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Remove another user (with fallback) | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Force password reset on another user | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Edit own role | — | — | — | — | — | — | — | — | rejected by `SELF_ROLE_MODIFICATION_FORBIDDEN` |
| Direct administrative user creation | — | — | — | — | — | ✓ + sign + reason | ✓ + sign + reason | — | platform-identity check; `ADMINISTRATIVE_PROVISIONING` audit |
| Read access-governance report | — | — | — | ✓ | ✓ | support / break-glass only | support / break-glass only | — | — |
| Export access-governance report | — | — | — | — | ✓ + sign | support / break-glass only | support / break-glass only | — | `tenant_admin_authority` |
| Read access-governance audit log entries | — | — | — | ✓ | ✓ | support / break-glass only | support / break-glass only | — | — |

There is **no `tenant_admin` base role**. The five tenant-level base roles are exactly `admin`, `quality_lead`, `reviewer`, `auditor`, and `viewer` (§3.2). Regulated administrative actions in this module are gated by base role `admin` plus the Authority Profile `tenant_admin_authority`. The two cross-tenant platform identities `platform_admin` and `super_admin` operate outside the tenant `memberships.role` enum.

External identities (URS-01 §3.2.3) cannot reach Module 2's administrative surface. System identities are not actors on user-invoked Module 2 routes.

#### 3.6.1 Platform-identity tenant actions — controlled support / break-glass posture

Platform identities (`platform_admin`, `super_admin`) MAY perform tenant-scoped Module 2 actions (invite, role change, deactivate, reactivate, remove, force password reset, matrix edit, governance export) **only under a controlled support / break-glass posture** that captures, on the same regulated administrative endpoint, all of the following: target tenant identifier, business-justification reason, support-ticket / customer-reference identifier, electronic signature, and an emit of `PLATFORM_TENANT_ACCESS_USED` to the URS-06 audit substrate plus a Security Operations Centre alert. Routine tenant administration is the responsibility of tenant `admin` users holding `tenant_admin_authority`; platform-identity use of these endpoints outside the support / break-glass envelope is rejected with `PLATFORM_TENANT_ACCESS_DENIED` (403) and audited as a forensic event. The intent is that a customer audit can identify every platform-staff intervention, the reason, the linked support ticket, and the signing platform identity.

---

## 4. End-to-End User Journeys

Each journey names trigger, preconditions, actor, entry screen, step-by-step UI, back-end transaction, validation, audit events, e-signature trigger where applicable, notifications, cross-module effects, and error / recovery paths.

### J-01 — Tenant administrator invites a new user

- Trigger: tenant administrator opens `/admin/users/invite`.
- Actor: `admin` holding `tenant_admin_authority`.
- Steps: form captures e-mail, intended base role, intended membership status (`active` or `pending`), reason; Controlled Approval Modal collects re-authentication, meaning of signature, reason; back end validates e-mail format and uniqueness within tenant, validates the role against the catalogue, writes `user_invitations` row with single-use token and seven-day time-to-live, emits `INVITE_SENT` and `ROLE_ASSIGNED_PENDING`, sends e-mail via URS-30. Subsequent acceptance is handled by URS-01 J-06.
- Validation: e-mail RFC 5322 + max 254; role in catalogue; reason min 8 chars.
- Audit events: `INVITE_SENT`, `ROLE_ASSIGNED_PENDING`.
- Notifications: outbound e-mail to invitee.
- Cross-module: URS-30 delivery; URS-01 accept-invite handoff; URS-06 audit ingest.
- Error paths: duplicate e-mail returns 409 `INVITE_DUPLICATE`; invalid role returns 400 `INVALID_ROLE`; e-mail send failure surfaces a non-blocking warning and queues retry per URS-30.

```mermaid
sequenceDiagram
  autonumber
  participant ADM as Administrator
  participant FE as Admin UI
  participant CAM as Controlled Approval Modal
  participant API as Access API
  participant DB as Database
  participant AUD as Audit Log
  participant N as URS-30 Notification

  ADM->>FE: /admin/users/invite (form)
  ADM->>CAM: re-auth + meaning + reason
  CAM->>API: POST /access/users/invite
  API->>DB: validate email, role
  alt invalid
    API-->>CAM: 400 / 409
  else valid
    API->>DB: insert user_invitations (token, ttl=7d)
    API->>AUD: INVITE_SENT + ROLE_ASSIGNED_PENDING
    API->>N: enqueue invite e-mail
    API-->>CAM: 201
  end
```

### J-02 — Invitee accepts invitation

Trigger and steps owned by URS-01 J-06. Module 2 receives the audit event `INVITE_ACCEPTED` and writes the `memberships` row when the back-end transaction commits. The role assigned at acceptance equals the role recorded on the `user_invitations` row at invitation time; later changes to the invitation row do not retroactively alter acceptance.

### J-03 — User views own effective permissions

- Trigger: user navigates to `/settings/permissions`.
- Steps: `useEffectivePermissions` calls `GET /access/me/permissions`; back end resolves the user's role and the tenant's matrix, computes the effective permissions, returns `{baseRole, effectivePermissions, tenantId, matrixVersion, claimsVersion}`; UI renders by resource group.
- Audit: read-only call; not audited beyond access logs.

### J-04 — Tenant administrator edits the permission matrix

- Trigger: tenant administrator navigates to `/admin/permissions`.
- Actor: `admin` holding `tenant_admin_authority`.
- Steps: matrix editor displays the permission matrix grouped by resource; rows for `admin` are visually disabled (Mode A invariant); the administrator toggles a non-`admin` cell, the change appears as a pending diff, the administrator clicks "Save", the Controlled Approval Modal collects re-authentication, meaning of signature, reason; back end validates resource and action against the taxonomy registry, rejects `admin`-row mutation, performs idempotent upsert, increments tenant matrix version, emits `PERMISSION_UPDATED` with old and new `is_allowed`.
- Validation: role in catalogue; resource and action in taxonomy registry; toggle has actually changed value (no audit on no-op upsert without a value change).
- Audit events: `PERMISSION_UPDATED` with before / after.
- Notifications: hourly matrix-edit digest to the tenant administrator queue when at least one matrix mutation occurred in the previous hour; tenant opt-out allowed through security policy.
- Cross-module: every regulated module re-resolves effective permissions on the next bootstrap; URS-22 inspection-export captures matrix version at any signed checkpoint.

### J-05 — Tenant administrator changes another user's base role

- Trigger: administrator opens `/admin/users/<id>/role`.
- Actor: `admin` holding `tenant_admin_authority`.
- Steps: form captures the new role and reason; Controlled Approval Modal signs; back end rejects self-modification, validates the new role against the catalogue, updates `memberships.role`, increments the user's claims version, revokes all sessions of the affected user via URS-01, emits `ROLE_ASSIGNED` and (if downgrade) `ROLE_DOWNGRADED` (in the URS-01 authority change log), and writes the cascade revoke as `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE`.
- Validation: target ≠ self; role in catalogue; reason min 8 chars.
- Audit events: `ROLE_ASSIGNED`, `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE`, optional `ROLE_DOWNGRADED`.
- Notifications: e-mail to the affected user.
- Cross-module: URS-01 session revocation cascade; URS-06 audit ingest.
- Error paths: self-modification → `SELF_ROLE_MODIFICATION_FORBIDDEN` (403); invalid role → 400 `INVALID_ROLE`.

```mermaid
sequenceDiagram
  autonumber
  participant ADM as Administrator
  participant FE as Admin UI
  participant CAM as Controlled Approval Modal
  participant API as Access API
  participant DB as Database
  participant SES as URS-01 Sessions
  participant ACL as Authority Change Log
  participant AUD as Audit Log

  ADM->>FE: /admin/users/<id>/role
  ADM->>CAM: re-auth + meaning + reason
  CAM->>API: PATCH /access/users/:id/role
  API->>API: reject if target = self
  API->>DB: validate role in catalogue
  API->>DB: BEGIN TX
  API->>DB: update memberships.role
  API->>AUD: ROLE_ASSIGNED (before/after)
  alt downgrade
    API->>ACL: ROLE_DOWNGRADED
  end
  API->>SES: revoke all sessions of user
  API->>AUD: SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE
  API->>DB: increment claims_version
  API->>DB: COMMIT
  API-->>CAM: 200
```

### J-06 — Tenant administrator deactivates a user

- Trigger: administrator opens user detail and clicks "Deactivate".
- Steps: form captures reason; Controlled Approval Modal signs; back end rejects self-deactivation, sets `users.status = 'inactive'` and `memberships.is_active = false`, emits `USER_DEACTIVATED`, revokes all sessions, increments claims version. Subsequent login by the user fails with `ACCOUNT_DEACTIVATED` (URS-01).
- Audit: `USER_DEACTIVATED`, `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE`.
- Notifications: e-mail to affected user (informational).

### J-07 — Tenant administrator reactivates a user

- Trigger: administrator opens an inactive user's detail and clicks "Reactivate".
- Steps: reason; Controlled Approval Modal; sets `users.status = 'active'` and `memberships.is_active = true`; emits `USER_REACTIVATED`. The user must complete their next login normally; if password expired, `PASSWORD_EXPIRED` triggers force-reset.
- Audit: `USER_REACTIVATED`.

### J-08 — Tenant administrator removes a user (with fallback)

- Trigger: administrator opens user detail and clicks "Remove".
- Steps: reason; Controlled Approval Modal; back end inspects regulated-record references; if any exists, falls back to deactivation and emits `USER_REMOVAL_FALLBACK_TO_DEACTIVATION` plus `USER_DEACTIVATED`; if none, hard-deletes the membership row and emits `USER_REMOVED`. The `users` row itself is soft-deleted; the user identifier is preserved for audit traceability.
- Audit: `USER_REMOVED` or `USER_REMOVAL_FALLBACK_TO_DEACTIVATION` + `USER_DEACTIVATED`.

### J-09 — Tenant administrator forces a password reset on another user

- Trigger: administrator opens user detail and clicks "Force password reset".
- Steps: reason; Controlled Approval Modal; back end sets `users.force_password_change = true`, revokes all sessions, emits `FORCE_PASSWORD_RESET` and `FORCED_PASSWORD_RESET_TRIGGERED`. The user is required to change password on next successful credential entry; URS-01 enforces the redirect.
- Audit: `FORCE_PASSWORD_RESET`, `FORCED_PASSWORD_RESET_TRIGGERED`, `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE`.

### J-10 — Platform administrator administratively provisions a user

- Trigger: platform administrator runs the supplier-onboarding or migration tool and POSTs a creation directly.
- Actor: `platform_admin` or `super_admin` only.
- Steps: payload includes e-mail, role, status (`active`), reason; Controlled Approval Modal; back end rejects if actor is not platform-tier, otherwise creates `users` row, creates `memberships` row, emits `ADMINISTRATIVE_PROVISIONING` with reason and source-tool identifier, sends a welcome e-mail to the user with a forced password-reset link.
- Audit: `ADMINISTRATIVE_PROVISIONING`, `ROLE_ASSIGNED`.

### J-11 — Tenant administrator generates the access-governance report

- Trigger: administrator opens `/admin/governance`.
- Steps: filter form captures range, optional user filter, optional event filter; back end queries the access-governance event subset of the audit log; UI renders paginated table with export controls; Export routed through the Controlled Approval Modal; back end produces a signed download URL with fifteen-minute time-to-live and an integrity manifest.
- Audit: `ACCESS_GOVERNANCE_REPORT_EXPORTED` for export only.

### J-12 — Auditor reads the role catalogue

- Steps: `GET /api/v1/roles` returns the role catalogue; UI renders read-only table with hierarchy order, mutability flag, intended use.

### J-13 — Auditor reads the tenant permission matrix (read-only)

- Steps: `GET /access/permissions/matrix` (read-only) returns the full matrix for the auditor's tenant; UI renders pivot table by resource.

### J-14 — Permission-denied path on regulated module entry

- Steps: a `viewer` user navigates directly to `/admin/permissions` URL; `RoleGuard` rejects at the route layer, the UI renders the access-denied state without an API call, no audit event is written for the front-end intercept.

### J-15 — Permission-denied path on a section action

- Steps: a `quality_lead` user opens a deviations page; `PermissionGuard` for `(deviations, export)` is `false`; the export button is disabled and a tooltip reads "Permission denied — `deviations:export`"; if a stray API call escapes to the back end, the RBAC hook rejects with `PERMISSION_DENIED` (403) and writes an `RBAC_DENIED` audit row for forensic review.

### J-16 — Validation-error path

- Steps: form submission with invalid input (e.g., role not in catalogue, reason too short, e-mail malformed) produces inline field errors; back end re-validates and returns 400 with field-level detail.

### J-17 — System-error and retry path

- Steps: 5xx response surfaces a non-blocking toast with correlation identifier and retry button; UI does not auto-retry mutations.

### J-18 — Cross-tab matrix change resolution

- Steps: tenant administrator A edits a matrix cell while tenant administrator B has the matrix editor open in another tab; on B's next refresh the matrix-version mismatch is detected and the editor displays a non-blocking banner "Matrix version updated by another administrator — refresh to continue editing"; B's pending diffs are preserved client-side until B confirms refresh.

### J-19 — Bulk role-change path

- Steps: administrator selects multiple users in `/admin/users` and chooses "Bulk role change"; modal warns of the cascade (each user's sessions revoked, claims-version increment, audit event); reason captured once and applied to each row's audit event with a shared `bulkOperationId`.
- Audit: one `ROLE_ASSIGNED` per user, all sharing `bulkOperationId`.

### J-20 — Periodic access review trigger

- Steps: per URS-01 §12.10, a scheduled reminder fires thirty days before the access-review window opens; the tenant administrator opens `/admin/governance` filtered to "Active assignments" and runs the review; each reviewed assignment is electronically signed; the review record is persisted into the access-governance event log under `ACCESS_REVIEW_COMPLETED` per assignment or as a single batched record (configurable per tenant).

### J-21 — Access-governance audit-trail review

- Steps: per URS-01 §12.11, the security operations centre or QA reviewer opens `/admin/governance/audit` (read-only) and reviews high-risk events (`ROLE_DOWNGRADED`, `ADMINISTRATIVE_PROVISIONING`, `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`); each triage decision is electronically signed; high-risk events are reviewed within one business day, routine events at least monthly.

### J-22 — Migration-mode role-array fallback

- Steps: during the matrix-driven migration, residual `RoleGuard` call sites in the front end still gate by static role arrays; the UI flags such gates with a developer-only indicator (`data-migration="role-array-legacy"`) so QA can audit the migration progress.
- Audit: not audited at runtime; tracked at code-review time only.

### J-23 — Permission action taxonomy registry change

- Steps: a Class 3 (additive) change to the permission action taxonomy is approved through URS-13; URS-04 wraps the change in an electronic-signature ceremony; the taxonomy registry is updated; the matrix UI surfaces the new action in the cell editor; existing rows are unaffected.

### J-24 — Self-modification denial path

- Steps: tenant administrator attempts to edit own permission row or own role; back end rejects with `SELF_ROLE_MODIFICATION_FORBIDDEN`; UI surfaces inline error in the editor.

### J-25 — Inviter retention after invitation expiry

- Steps: an invitation expires unaccepted after seven days; the `user_invitations` row is marked `expired`; the inviter identifier is retained for audit; if the same e-mail is re-invited, a new row is created with a fresh token; the prior invitation row remains for audit traceability.

---

## 5. Front-End Expected State

### 5.1 Navigation and information architecture

| Route | Purpose | Guards |
|---|---|---|
| `/settings/permissions` | View own effective permissions | authenticated |
| `/admin/users` | List users in tenant | authenticated, `RoleGuard` admin tier |
| `/admin/users/:id` | User detail — role, status, sessions, MFA, governance history | as above |
| `/admin/users/invite` | Invite a new user | as above + `tenant_admin_authority` |
| `/admin/users/:id/role` | Change user role (modal) | as above |
| `/admin/users/:id/deactivate` | Deactivate user (modal) | as above |
| `/admin/users/:id/reactivate` | Reactivate user (modal) | as above |
| `/admin/users/:id/remove` | Remove user (modal) | as above |
| `/admin/users/:id/force-password-reset` | Force password reset (modal) | as above |
| `/admin/permissions` | Permission-matrix editor | as above |
| `/admin/roles` | Role-catalogue read-only viewer | authenticated, `RoleGuard` admin tier |
| `/admin/governance` | Access-governance report | authenticated, `RoleGuard` admin tier or `auditor` |
| `/admin/governance/audit` | Access-governance audit-trail viewer (read-only) | authenticated, `RoleGuard` admin tier or `auditor` |
| `/admin/users/provision` | Direct administrative provisioning (platform-only) | authenticated, `RoleGuard` `platform_admin` / `super_admin` |

### 5.2 Screens

#### `/settings/permissions` — own effective permissions

- Purpose: read-only view of the caller's effective permissions for inspection or self-audit.
- Fields: read-only resource × action grid grouped by resource; matrix-version badge; claims-version badge.
- Loading: skeleton grid until `useEffectivePermissions` resolves.
- Error: standard error toast with retry.
- Success: full grid with permitted actions checked.
- Empty: not applicable (every authenticated user has at least read on something).
- Permission-restricted: not applicable.
- Audit visibility: no audit row written for self-read.

#### `/admin/users` — user list

- Purpose: list users in the tenant; entry to user detail and bulk operations.
- Columns: user (avatar + name + e-mail), role, status, last active, multi-factor enrolled, scope summary (counts of Authority Profiles), action menu.
- Filters: status (active / pending / inactive), role, search-by-name.
- Bulk: bulk role-change, bulk deactivate, bulk force password reset (each routed through the Controlled Approval Modal).
- States: loading, error, success, empty ("no users in tenant — invite the first one"), permission-restricted.
- Audit visibility: read-only.

#### `/admin/users/:id` — user detail

- Purpose: single source of truth for a user's role, status, sessions, MFA, governance history.
- Tabs: Overview, Permissions (read-only effective), Sessions (delegated to URS-01), MFA (delegated), Authority Profiles (delegated to URS-05), Governance history (this module's events).
- Actions: Change role, Deactivate, Reactivate, Remove, Force password reset; each opens its own modal.

#### `/admin/users/invite` — invitation form

- Fields: e-mail (required), base role (required, from catalogue), reason (required, min 8 chars), optional message to invitee.
- Conditional display: pending-invitations warning if e-mail is already invited.
- States: standard.

#### `/admin/users/:id/role` — role-change modal

- Fields: new role (required, from catalogue), reason (required, min 8 chars).
- Confirmation banner: "Changing role will revoke all active sessions of [user] and increment claims version."

#### `/admin/users/:id/deactivate` and `.../reactivate`

- Fields: reason (required).
- Confirmation banner: deactivation revokes sessions; reactivation does not auto-restore Authority Profiles.

#### `/admin/users/:id/remove`

- Fields: reason (required).
- Confirmation banner: "If [user] has authored regulated records the system will fall back to deactivation; the user identifier is preserved for audit."

#### `/admin/users/:id/force-password-reset`

- Fields: reason (required).
- Confirmation banner: "Sessions will be revoked and the user must set a new password before any further regulated action."

#### `/admin/permissions` — permission-matrix editor

- Layout: pivot table; rows = `(role, resource)`; columns = action verbs; cells = checkboxes for `is_allowed`.
- Conditional display: `admin` rows are disabled (Mode A invariant); pending diffs are highlighted in amber until saved; matrix-version banner displays the current and last-modified-by.
- Actions: "Save changes" routes the diff through the Controlled Approval Modal with a single signature covering the diff.
- States: loading skeleton; error; success (toast + reset of pending diffs); empty (every freshly-created tenant has the seed matrix populated, so empty is rare).
- Permission-restricted: `auditor` sees the read-only view; only an `admin` holding `tenant_admin_authority` may save.

#### `/admin/roles` — role-catalogue viewer

- Read-only table with role key, label, description, hierarchy order, mutability flag, intended use.

#### `/admin/governance` — access-governance report

- Filters: range, user, event subset, role.
- Columns: timestamp, actor, target user, event, before / after, reason, electronic-signature identifier where linked.
- Actions: "Export" routes through the Controlled Approval Modal.

#### `/admin/governance/audit` — access-governance audit-trail viewer

- Read-only view of the access-governance event subset of the audit log; integrity-badge column for hash-chain verification of the event window.

#### `/admin/users/provision` — direct administrative provisioning

- Restricted to `platform_admin` / `super_admin`; not visible to tenant administrators; payload includes e-mail, role, status, reason, source-tool identifier.

### 5.3 Forms and field rules

| Field identifier | Label | Type | Required | Validation | Source | Editability by status | Audit requirement | Regulatory relevance |
|---|---|---|---|---|---|---|---|---|
| `inviteEmail` | E-mail | string | Yes | RFC 5322 + `.email()` + `.max(254)` + uniqueness within tenant | user input | invite | yes (`INVITE_SENT`) | 21 CFR Part 11 §11.10(d) |
| `inviteRole` | Base role | enum | Yes | `.enum(['admin','quality_lead','reviewer','auditor','viewer'])` | catalogue | invite | yes | 21 CFR Part 11 §11.10(d) |
| `inviteReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | invite | yes | 21 CFR Part 11 §11.10(e) |
| `inviteMessage` | Message to invitee | string | No | `.max(1000)` | user input | invite | yes (recorded but content excluded from public exports) | EU GMP Annex 11 §12 |
| `roleChangeNewRole` | New role | enum | Yes | as `inviteRole` | catalogue | role change | yes | 21 CFR Part 11 §11.10(d) |
| `roleChangeReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | role change | yes | 21 CFR Part 11 §11.10(e) |
| `deactivateReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | deactivate | yes | 21 CFR Part 11 §11.10(d) |
| `reactivateReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | reactivate | yes | 21 CFR Part 11 §11.10(d) |
| `removeReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | remove | yes | 21 CFR Part 11 §11.10(d) |
| `forceResetReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | force password reset | yes | 21 CFR Part 11 §11.300 |
| `matrixCell.isAllowed` | Permission cell | boolean | Yes | strict boolean | matrix editor | matrix edit | yes (`PERMISSION_UPDATED` with before / after) | EU GMP Annex 11 §12 |
| `matrixDiffReason` | Reason for matrix diff | string | Yes | `.min(8).max(2000)` | user input | matrix edit | yes | 21 CFR Part 11 §11.10(e) |
| `provisionEmail` | E-mail | string | Yes | as `inviteEmail` | user input | direct provisioning | yes (`ADMINISTRATIVE_PROVISIONING`) | 21 CFR Part 11 §11.10(d) |
| `provisionSourceTool` | Source tool | string | Yes | `.min(2).max(120)` | client envelope | direct provisioning | yes | EU GMP Annex 11 §3 (supplier oversight) |
| `provisionReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | direct provisioning | yes | 21 CFR Part 11 §11.10(e) |
| `governanceFilterRange` | Range | datetime range | Yes | start ≤ end | user input | governance read / export | yes (export only) | EU GMP Annex 11 §9 |
| `governanceExportFormat` | Format | enum | Yes | `'csv' | 'pdf' | 'jsonl'` | user input | governance export | yes | 21 CFR Part 11 §11.10(e) |
| `governanceExportReason` | Reason | string | Yes | `.min(8).max(2000)` | user input | governance export | yes | 21 CFR Part 11 §11.10(e) |

### 5.4 UI state machines

#### Membership lifecycle

```mermaid
stateDiagram-v2
  [*] --> pending: invitation issued
  pending --> active: invitation accepted (URS-01)
  pending --> expired: invitation TTL elapsed
  active --> inactive: deactivate (electronic signature)
  inactive --> active: reactivate (electronic signature)
  active --> removed: remove (electronic signature; if no regulated references)
  active --> inactive: remove fallback (electronic signature; with `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`)
  removed --> [*]
  expired --> [*]
```

#### Permission-matrix row lifecycle

```mermaid
stateDiagram-v2
  [*] --> seeded: tenant created (default matrix populated)
  seeded --> mutated: PERMISSION_UPDATED (electronic signature)
  mutated --> mutated: PERMISSION_UPDATED (electronic signature)
  mutated --> [*]
```

### 5.5 UX safety controls

- Confirmation modals on every irreversible action (deactivation, removal, force password reset, role downgrade) name the user and the cascade effect.
- Submit controls disabled during async submission to prevent double-submission.
- Duplicate detection on invitation form (warns if the e-mail has a pending invitation or an active membership).
- Stale-version warning on matrix editor (banner if the tenant matrix version has advanced since the editor opened).
- Self-modification block surfaced inline in user-detail action menu (the action is disabled with tooltip "Self-modification is not permitted").
- Mode A admin invariant surfaced inline in matrix editor (`admin` rows are visually disabled).
- Bulk-action warnings name the count of affected users and the cascade.
- Regulated-decision warning on every Controlled Approval Modal explains the meaning of signature.

---

## 6. Back-End Expected State

### 6.1 Domain entities

- `users` (read-only here; primary write owned by URS-01 / URS-08).
- `memberships`.
- `user_invitations`.
- `permission_matrix`.
- `permission_resources` (taxonomy registry).
- `permission_actions` (taxonomy registry).
- `role_catalogue` (shared constant + DB read endpoint).
- `tenant_matrix_version` (per-tenant counter).
- `audit_log` (consumed; primary owner URS-06) with the access-governance event subset.
- `bulk_operations` (correlation envelope for bulk role-changes / deactivations).

### 6.1.1 Diagram 6.1-A — Module 2 data model (entity-relationship overview)

```mermaid
erDiagram
  TENANTS ||--o{ MEMBERSHIPS : has
  USERS ||--o{ MEMBERSHIPS : has
  TENANTS ||--o{ USER_INVITATIONS : has
  USERS ||--o{ USER_INVITATIONS : invitee
  USERS ||--o{ USER_INVITATIONS : inviter
  TENANTS ||--o{ PERMISSION_MATRIX : has
  TENANTS ||--|| TENANT_MATRIX_VERSION : counter
  PERMISSION_RESOURCES ||--o{ PERMISSION_MATRIX : gates
  PERMISSION_ACTIONS ||--o{ PERMISSION_MATRIX : gates
  ROLE_CATALOGUE ||--o{ PERMISSION_MATRIX : indexes
  ROLE_CATALOGUE ||--o{ MEMBERSHIPS : indexes
  USERS ||--o{ AUDIT_LOG : actor
  USERS ||--o{ AUDIT_LOG : target
  BULK_OPERATIONS ||--o{ AUDIT_LOG : correlates

  MEMBERSHIPS {
    uuid id PK
    uuid user_id FK
    uuid tenant_id FK
    string role
    string status
    bool is_active
    string external_type
    timestamptz created_at
    timestamptz updated_at
  }
  USER_INVITATIONS {
    uuid id PK
    uuid tenant_id FK
    uuid inviter_user_id FK
    citext email
    string role
    string token_hash
    timestamptz expires_at
    timestamptz accepted_at
    string status
  }
  PERMISSION_MATRIX {
    uuid id PK
    uuid tenant_id FK
    string role
    string resource FK
    string action FK
    bool is_allowed
    int matrix_version
    uuid updated_by FK
    timestamptz updated_at
  }
  TENANT_MATRIX_VERSION {
    uuid tenant_id FK
    int matrix_version
    timestamptz last_incremented_at
  }
  ROLE_CATALOGUE {
    string key PK
    string label
    string description
    int hierarchy_order
    bool is_mutable
  }
  PERMISSION_RESOURCES {
    string key PK
    string label
    string description
  }
  PERMISSION_ACTIONS {
    string key PK
    string label
    string description
  }
  BULK_OPERATIONS {
    uuid id PK
    uuid tenant_id FK
    uuid actor_user_id FK
    string operation
    int target_count
    string reason
    uuid e_sig_id FK
    timestamptz created_at
  }
```

### 6.1.2 Launch action taxonomy

The launch list of `permission_actions.key` values (registered in the taxonomy registry on tenant seed) is fixed below. Adding an action is a Class 3 change (additive) and proceeds through the normal URS-13 gate. Renaming or removing an action is Class 1 and requires consumer migration.

`read`, `read_executive`, `read_operational`, `read_tactical`, `read_sensitive`, `list`, `create`, `update`, `delete`, `submit`, `review`, `approve`, `reject`, `reopen`, `void`, `archive`, `restore`, `export`, `download`, `generate`, `link`, `unlink`, `manage`, `configure`, `approve_config`, `manage_users`, `manage_roles`, `classify`, `escalate`, `override`, `purge`, `release`, `dispatch`, `quarantine`, `recall`, `write`.

### 6.1.3 Launch resource taxonomy

The launch list of `permission_resources.key` values (registered on tenant seed) is fixed below. Resources match the Verixa module map with the addition of administrative areas (`users`, `permissions`, `roles`, `governance`, `audit`, `authority`). Adding a resource is a Class 3 change.

`users`, `permissions`, `roles`, `governance`, `audit`, `authority`, `tenants`, `sites`, `products`, `studies`, `suppliers`, `documents`, `change_control`, `complaints`, `oos`, `deviations`, `rca`, `capas`, `risks`, `reviews`, `findings`, `inspections`, `batch_records`, `stability`, `environmental_monitoring`, `apqr`, `regulatory`, `training`, `screen_reader`, `notifications`, `dqg`, `mira_ai`, `gmp`, `gdp`, `infrastructure`.

### 6.1.4 Launch source-tool registry for direct administrative provisioning

Direct administrative provisioning at `POST /access/users/provision` is permitted only when the request envelope's `sourceTool` field is one of the following registered values. Adding a source tool is a Class 1 change requiring founder electronic signature.

`migration-tool`, `tenant-onboarding-tool`, `supplier-onboarding-tool`, `cro-onboarding-tool`, `inspection-portal-bootstrap`, `customer-portal-bootstrap`.

### 6.1.5 Removal-fallback trigger graph

The removal endpoint MUST consult the regulated-record reference graph and fall back to deactivation if any of the following conditions hold for the target user identifier in the tenant: any row exists where the user is `created_by`, `updated_by`, `actor_user_id`, `signed_by`, `target_user_id`, `assigned_to`, `owner_id`, `investigator_id`, `classified_by`, `closed_by`, `released_by`, `dispatched_by`, or `reviewed_by` in the tables maintained by URS-12 (documents and revisions), URS-13, URS-14 (complaints), URS-15 (OOS / OOT), URS-16 (deviations), URS-17 (RCAs), URS-18 (CAPAs), URS-19 (risk assessments), URS-20 (reviews), URS-21 (findings), URS-22 (inspections), URS-23 (batch records and dispositions), URS-24 (stability), URS-25 (environmental monitoring excursions), URS-26 (APQRs), URS-27 (regulatory submissions), URS-28 (training records), URS-29 (data extractions), URS-31 (DQG artefacts), URS-33 (GMP records), URS-34 (GDP records), or in any electronic-signature row owned by URS-04. The graph is interpreted at execution time; no pre-computed materialised view is required for correctness.

### 6.2 Data model requirements

| Entity | Purpose | Key fields | Required | Unique | Tenant isolation | Versioning | Retention | Soft-delete | Audit | E-sig link |
|---|---|---|---|---|---|---|---|---|---|---|
| `memberships` | User × tenant × base role | `id`, `user_id`, `tenant_id`, `role`, `status`, `is_active`, `external_type` (nullable) | all | unique(`user_id`, `tenant_id`) | RLS on `tenant_id` | none | seven years | yes (`is_active=false`) | yes | yes (role / status changes) |
| `user_invitations` | Invitation lifecycle | `id`, `tenant_id`, `inviter_user_id`, `email`, `role`, `token_hash`, `expires_at`, `accepted_at`, `status` | all | unique(`token_hash`); unique active(`tenant_id`, lower(`email`)) | RLS on `tenant_id` | none | seven years | hard after retention; expired rows retained for audit | yes | yes (invite send / accept) |
| `permission_matrix` | Tenant permission tuples | `id`, `tenant_id`, `role`, `resource`, `action`, `is_allowed`, `matrix_version`, `updated_by`, `updated_at` | all | unique(`tenant_id`, `role`, `resource`, `action`) | RLS on `tenant_id` | counter-versioned | retain all versions through the audit log | not applicable | yes | yes (every mutation) |
| `tenant_matrix_version` | Per-tenant monotonic counter | `tenant_id`, `matrix_version`, `last_incremented_at` | all | unique(`tenant_id`) | RLS on `tenant_id` | counter | retain | not applicable | yes (counter increments) | not applicable |
| `role_catalogue` | Shared role registry | `key`, `label`, `description`, `hierarchy_order`, `is_mutable` | all | unique(`key`) | platform-context (global) | versioned | retain | not applicable | yes (catalogue edits via platform-only API) | yes |
| `permission_resources` | Taxonomy registry | `key`, `label`, `description` | all | unique(`key`) | platform-context | versioned | retain | yes | yes | yes (taxonomy edits) |
| `permission_actions` | Taxonomy registry | `key`, `label`, `description` | all | unique(`key`) | platform-context | versioned | retain | yes | yes | yes (taxonomy edits) |
| `bulk_operations` | Correlation envelope | `id`, `tenant_id`, `actor_user_id`, `operation`, `target_count`, `reason`, `e_sig_id`, `created_at` | all | none | RLS on `tenant_id` | none | seven years | not applicable | yes | yes |
| `audit_log` (subset) | Access-governance events | inherited per URS-06 | as URS-06 | as URS-06 | as URS-06 | append-only | seven years | append-only | self | yes |

### 6.3 API requirements

#### 6.3.1 Effective permissions

| Method | Endpoint | Actor | Request | Response | Permission | Audit | Error codes | Rate |
|---|---|---|---|---|---|---|---|---|
| GET | `/access/me/permissions` | authenticated | none | `{baseRole, effectivePermissions, tenantId, matrixVersion, claimsVersion}` | self | none | none | 60/min/user |

#### 6.3.2 Role catalogue

| Method | Endpoint | Actor | Request | Response | Permission | Audit | Error codes |
|---|---|---|---|---|---|---|---|
| GET | `/api/v1/roles` | authenticated | none | `RoleCatalogueEntry[]` | self | none | none |
| PATCH | `/api/v1/roles/:key` | platform-administrator | `{label?, description?}` (electronic-signed) — `key` and `hierarchy_order` are immutable after launch | `200 RoleCatalogueEntry` | platform admin | `ROLE_CATALOGUE_UPDATED` | `ROLE_KEY_UNKNOWN`, `IMMUTABLE_FIELD_REJECTED` |

`key` and `hierarchy_order` are immutable after launch because role-downgrade detection and `ROLE_DOWNGRADED` audit semantics depend on stable hierarchy. `label` and `description` may be changed as Class 4 (configuration). Any hierarchy change is **Class 1**, requires Founder + QA + Regulatory Affairs + Information Security approval, a migration impact assessment, full Operational Qualification regression, and Performance Qualification on the role-change cascade.

#### 6.3.3 Permission matrix

| Method | Endpoint | Actor | Request | Response | Permission | Audit | Error codes |
|---|---|---|---|---|---|---|---|
| GET | `/access/permissions/matrix` | administrator / auditor | filters | `PermissionMatrixRow[]` | `tenant_admin_authority` (write) / read for `auditor` and `admin` | none | none |
| PATCH | `/access/permissions/matrix` | administrator | `{rows: [{role, resource, action, is_allowed}, …], reason}` (electronic-signed) | `200 {matrixVersion, updated: PermissionMatrixRow[]}` | `tenant_admin_authority` | `PERMISSION_UPDATED` per row + `MATRIX_VERSION_INCREMENTED` once | `INVALID_ROLE`, `INVALID_RESOURCE`, `INVALID_ACTION`, `ADMIN_IMMUTABLE`, `SELF_ROLE_MODIFICATION_FORBIDDEN`, `STALE_VERSION` |
| GET | `/access/permissions/resources` | administrator / auditor | none | `Resource[]` | `tenant_admin_authority` (write) / read | none | none |
| GET | `/access/permissions/actions` | administrator / auditor | none | `Action[]` | as above | none | none |
| POST | `/access/permissions/resources` | platform-administrator | `Resource` (electronic-signed) | `201` | platform admin | `PERMISSION_RESOURCE_ADDED` | `RESOURCE_KEY_DUPLICATE` |
| POST | `/access/permissions/actions` | platform-administrator | `Action` (electronic-signed) | `201` | platform admin | `PERMISSION_ACTION_ADDED` | `ACTION_KEY_DUPLICATE` |

#### 6.3.4 User access lifecycle

| Method | Endpoint | Actor | Request | Response | Permission | Audit | Error codes |
|---|---|---|---|---|---|---|---|
| POST | `/access/users/invite` | administrator | `{email, role, message?, reason}` (electronic-signed) | `201 UserInvitation` | `tenant_admin_authority` | `INVITE_SENT`, `ROLE_ASSIGNED_PENDING` | `INVITE_DUPLICATE`, `INVALID_ROLE`, `INVALID_EMAIL` |
| GET | `/access/users` | administrator / auditor | filters | `User[]` | `tenant_admin_authority` (write) / read for `auditor` and `admin` | none | none |
| GET | `/access/users/:id` | administrator / auditor | none | `UserDetail` | as above | none | `USER_NOT_FOUND` |
| PATCH | `/access/users/:id/role` | administrator | `{role, reason}` (electronic-signed) | `200 User` | `tenant_admin_authority` | `ROLE_ASSIGNED` (+ `ROLE_DOWNGRADED` if downgrade) + `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE` | `INVALID_ROLE`, `SELF_ROLE_MODIFICATION_FORBIDDEN`, `USER_NOT_FOUND` |
| POST | `/access/users/:id/deactivate` | administrator | `{reason}` (electronic-signed) | `204` | `tenant_admin_authority` | `USER_DEACTIVATED` + `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE` | `SELF_ROLE_MODIFICATION_FORBIDDEN`, `USER_NOT_FOUND` |
| POST | `/access/users/:id/reactivate` | administrator | `{reason}` (electronic-signed) | `204` | `tenant_admin_authority` | `USER_REACTIVATED` | `USER_NOT_FOUND`, `USER_ALREADY_ACTIVE` |
| DELETE | `/access/users/:id/remove` | administrator | `{reason}` (electronic-signed) | `204` | `tenant_admin_authority` | `USER_REMOVED` or `USER_REMOVAL_FALLBACK_TO_DEACTIVATION` + `USER_DEACTIVATED` | `USER_NOT_FOUND`, `SELF_ROLE_MODIFICATION_FORBIDDEN` |
| POST | `/access/users/:id/force-password-reset` | administrator | `{reason}` (electronic-signed) | `204` | `tenant_admin_authority` | `FORCE_PASSWORD_RESET`, `FORCED_PASSWORD_RESET_TRIGGERED`, `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE` | `USER_NOT_FOUND` |
| POST | `/access/users/provision` | platform-administrator | `{email, role, status, reason, sourceTool}` (electronic-signed) | `201 User` | `platform_admin` / `super_admin` | `ADMINISTRATIVE_PROVISIONING`, `ROLE_ASSIGNED` | `ADMIN_PROVISIONING_REQUIRED`, `INVITE_DUPLICATE`, `INVALID_ROLE`, `INVALID_EMAIL` |

#### 6.3.5 Bulk operations

| Method | Endpoint | Actor | Request | Response | Permission | Audit |
|---|---|---|---|---|---|---|
| POST | `/access/users/bulk/role` | administrator | `{userIds[], role, reason}` (electronic-signed) | `200 {bulkOperationId, processed[]}` | `tenant_admin_authority` | one `ROLE_ASSIGNED` per user with shared `bulkOperationId` |
| POST | `/access/users/bulk/deactivate` | administrator | `{userIds[], reason}` (electronic-signed) | `200 {bulkOperationId, processed[]}` | `tenant_admin_authority` | one `USER_DEACTIVATED` per user with shared `bulkOperationId` |
| POST | `/access/users/bulk/force-password-reset` | administrator | `{userIds[], reason}` (electronic-signed) | `200 {bulkOperationId, processed[]}` | `tenant_admin_authority` | one `FORCE_PASSWORD_RESET` per user with shared `bulkOperationId` |

#### 6.3.6 Access-governance reporting

| Method | Endpoint | Actor | Request | Response | Permission | Audit |
|---|---|---|---|---|---|---|
| GET | `/access/governance/report` | administrator / auditor | filters | `GovernanceEvent[]` | as above | none |
| POST | `/access/governance/report/export` | administrator | `{range, filters, format}` (electronic-signed) | signed download URL with TTL fifteen minutes | `tenant_admin_authority` | `ACCESS_GOVERNANCE_REPORT_EXPORTED` |
| GET | `/access/governance/audit` | administrator / auditor | filters | hash-chain audit rows + integrity manifest | as above | none |

### 6.4 Workflow engine requirements

| Workflow | Step | Time-to-live or timer | Auto-action | Reminder |
|---|---|---|---|---|
| Invitation | issued → accepted | seven days | invitation expires; status `expired`; emit `INVITE_EXPIRED` | T-1 day reminder e-mail to invitee |
| Bulk operation | submitted → completed | sixty seconds soft target; rows processed sequentially in transaction batches | batch transaction commits per row; partial failure rolls back the affected batch only | none |
| Periodic access review | per URS-01 §12.10 | quarterly (GxP-critical tenants) / semi-annually | reminder thirty days before window opens (per URS-30) | as URS-01 §12.10 |
| Periodic audit-trail review | per URS-01 §12.11 | high-risk events: one business day; routine: monthly | reviewer-attached triage decision (electronic-signed) | as URS-01 §12.11 |
| Matrix-edit conflict | competing edits in two browser tabs | immediate | second attempt receives `STALE_VERSION` and the editor refreshes pending diffs | none |
| Effective-permissions resolver cache | per (`user_id`, `tenant_id`, `matrix_version`, `claims_version`) | thirty-second time-to-live | invalidated on `MATRIX_VERSION_INCREMENTED` and on `CLAIMS_VERSION_INCREMENTED` events delivered via the URS-06 event bus | none |
| Bulk-operation cap | per submission | two hundred users maximum | submissions exceeding the cap receive HTTP 400 `BULK_LIMIT_EXCEEDED`; the user interface offers automatic split into ≤ 200-user batches | none |
| Matrix-edit digest e-mail | per tenant | hourly when at least one matrix mutation occurred in the previous hour | digest sent to the tenant administrator queue via URS-30; opt-out per tenant via security policy | none |

### 6.5 Business rules

- **BR-02-01** — The base role enum on `memberships.role` is exactly `{admin, quality_lead, reviewer, auditor, viewer}`; any value outside this set MUST be rejected at the database CHECK constraint and the API.
- **BR-02-02** — Mode A admin invariant: any mutation targeting `(role='admin', …)` in `permission_matrix` MUST be rejected with `ADMIN_IMMUTABLE` and audited.
- **BR-02-03** — Self-modification block: any user-invoked mutation where `actor.user_id == target.user_id` for role / membership / matrix-row owned-by changes MUST be rejected with `SELF_ROLE_MODIFICATION_FORBIDDEN` and audited.
- **BR-02-04** — Permission resource and action MUST exist in the taxonomy registry at the moment of the mutation; otherwise reject with `INVALID_RESOURCE` / `INVALID_ACTION`.
- **BR-02-05** — Permission-matrix mutation MUST be idempotent: an upsert that does not change `is_allowed` returns 200 without writing an audit row or incrementing the matrix version.
- **BR-02-06** — Matrix version increments atomically with the matrix-row mutation in the same database transaction; failure to increment the counter rolls back the mutation.
- **BR-02-07** — Role change MUST revoke all sessions of the affected user via the URS-01 `revokeAllSessionsForUser` path with reason `'role_change'`; cascade is part of the same transaction (audit row written with `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE`).
- **BR-02-08** — Role downgrade (new role hierarchy < old role hierarchy) MUST additionally write `ROLE_DOWNGRADED` to the URS-01 authority change log under the actor's identity.
- **BR-02-09** — Invitation MUST require a unique active invitation per `(tenant_id, lower(email))`; a second invitation while the first is still active is rejected with `INVITE_DUPLICATE`.
- **BR-02-10** — Removal MUST consult the regulated-record reference graph; if any reference exists, fall back to deactivation with `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`.
- **BR-02-11** — Direct administrative provisioning MUST require the actor to hold `platform_admin` or `super_admin` identity; tenant administrators MUST receive `ADMIN_PROVISIONING_REQUIRED` (403).
- **BR-02-12** — Every regulated administrative action MUST be electronically signed via the Controlled Approval Modal; the front end never sends `ip` / `userAgent` / `timestamp` / `performedBy`.
- **BR-02-13** — Every governance event MUST be hash-chained into the URS-06 audit substrate; audit-write failure rolls back the originating action with `AUDIT_TRAIL_WRITE_FAILED`.
- **BR-02-14** — Effective permissions resolution MUST be deterministic and side-effect-free; the resolver MUST be cached per `(user_id, tenant_id, matrix_version, claims_version)` tuple.
- **BR-02-15** — Inline string-equality role checks (`request.user.role !== 'admin'`) outside the documented exemption list MUST be removed from production code; the documented exemption list is published in the engineering URS-13 register.

#### Diagram 6.5-A — Permission-matrix mutation transaction (single transaction, no partial commits)

```mermaid
sequenceDiagram
  autonumber
  participant ADM as Administrator
  participant CAM as Controlled Approval Modal
  participant API as Access API
  participant TAX as Taxonomy Registry
  participant DB as Database (single transaction)
  participant ESIG as Electronic Signature (URS-04)
  participant AUD as Audit Log (URS-06)

  ADM->>CAM: re-auth + meaning + reason
  CAM->>API: PATCH /access/permissions/matrix (rows + reason)
  API->>API: validate actor permission and base role
  API->>API: validate Authority Profile = tenant_admin_authority
  API->>TAX: validate role / resource / action against taxonomy registry
  alt invalid
    API-->>CAM: 400 INVALID_ROLE / INVALID_RESOURCE / INVALID_ACTION
  else valid
    API->>API: reject if any row targets `admin` (ADMIN_IMMUTABLE)
    API->>API: reject if actor.user_id == target.user_id (SELF_ROLE_MODIFICATION_FORBIDDEN)
    API->>DB: BEGIN TRANSACTION
    API->>DB: idempotent upsert for each row (only rows whose value changes)
    API->>DB: increment tenant_matrix_version once
    API->>ESIG: insert electronic-signature row (server-derived metadata)
    API->>AUD: append PERMISSION_UPDATED rows + MATRIX_VERSION_INCREMENTED (hash-chained)
    alt audit write fails
      API->>DB: ROLLBACK
      API-->>CAM: 500 AUDIT_TRAIL_WRITE_FAILED
    else audit write succeeds
      API->>DB: COMMIT
      API-->>CAM: 200 {matrixVersion, updated[]}
    end
  end
```

### 6.6 Audit trail requirements

The access-governance event vocabulary listed below is the canonical launch list; every code MUST have at least one writer and one regression test. Adding a code is a Class 3 change per §7.2.

`INVITE_SENT`, `INVITE_ACCEPTED` (mirrored from URS-01), `INVITE_EXPIRED`, `INVITE_DUPLICATE_REJECTED`, `ROLE_ASSIGNED_PENDING`, `ROLE_ASSIGNED`, `ROLE_DOWNGRADED` (mirrored to authority change log), `USER_DEACTIVATED`, `USER_REACTIVATED`, `USER_REMOVED`, `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`, `FORCE_PASSWORD_RESET`, `FORCED_PASSWORD_RESET_TRIGGERED`, `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE`, `PERMISSION_UPDATED`, `MATRIX_VERSION_INCREMENTED`, `PERMISSION_RESOURCE_ADDED`, `PERMISSION_ACTION_ADDED`, `ROLE_CATALOGUE_UPDATED`, `ADMINISTRATIVE_PROVISIONING`, `ACCESS_GOVERNANCE_REPORT_EXPORTED`, `RBAC_DENIED` (a forensic event written when a stray API call escapes the front-end guard), `ACCESS_REVIEW_COMPLETED` (per-attestation closure of a periodic access review window per §12.7), `ACCESS_REVIEW_EXCEPTION_RECORDED` (per-exception flagged during a periodic access review with linkage to the resulting CAPA or security deviation), `ACCESS_GOVERNANCE_AUDIT_REVIEW_COMPLETED` (per-window closure of a periodic audit-trail review per §12.8), `ACCESS_GOVERNANCE_AUDIT_REVIEW_EXCEPTION_RECORDED` (per-event triage decision — security incident / deviation / CAPA / no-impact — captured with electronic signature and linked to the reviewed audit row), `PLATFORM_TENANT_ACCESS_USED` (a platform-identity has performed a tenant-scoped Module 2 action under the controlled support / break-glass posture of §3.6.1).

Signature-row invalidation is owned by URS-04 and emits `SIGNATURE_INVALIDATED` to the URS-06 audit substrate; Module 2 records the linkage in its own action row through the electronic-signature identifier when a Module 2 record is mutated post-signature.

Audit-log write failure rolls back the originating action per BR-02-13; the diagram is identical in shape to URS-01 §6.6.

### 6.7 Electronic signature requirements

Every regulated administrative action in this module is electronically signed via the URS-04 Controlled Approval Modal. Module-specific signature meaning examples:

| Action | Signature meaning |
|---|---|
| Invite user | "I invite [email] as [role] for tenant [X]" |
| Change role | "I change the role of [user] from [oldRole] to [newRole]" |
| Deactivate | "I deactivate [user] for reason [R]" |
| Reactivate | "I reactivate [user] for reason [R]" |
| Remove | "I remove [user] (or fall back to deactivation) for reason [R]" |
| Force password reset | "I force [user] to change their password on next login" |
| Permission matrix edit | "I update permission rows: [diff summary] for tenant [X]" |
| Administrative provisioning | "I directly provision [email] as [role] for tenant [X] using [sourceTool]" |
| Governance export | "I export the access-governance report [range / filters] for purpose [P]" |

Signature row carries server-derived `ip`, `user_agent`, `signed_at`, `signed_by`. Signature is linked to the action row through the electronic-signature identifier; subsequent change to the signed entity invalidates the signature (sets `signature.invalidated_at` and writes `SIGNATURE_INVALIDATED`). Re-authentication is mandatory; multi-factor step-up is required for matrix edits and administrative provisioning.

### 6.8 Record versioning and class-of-change governance

- Versioned: `permission_matrix` (counter-versioned per tenant), `role_catalogue` (versioned), `permission_resources` and `permission_actions` taxonomy registries (versioned).
- Append-only logs: `audit_log` access-governance subset.
- Soft-delete: `memberships` (`is_active = false`).
- Hard-delete: ephemeral `user_invitations` rows after retention; expired tokens never reused.

---

## 7. Cross-Module Wiring and Change Impact

### 7.1 Cross-module wiring

#### Diagram 7-A — Module 2 substrate consumed by other modules

```mermaid
graph LR
  subgraph M2 [Module 2 — RBAC & Permissions]
    PM[Permission Matrix]
    RC[Role Catalogue]
    EP[Effective Permissions]
    PG[PermissionGuard / RoleGuard]
    GE[Governance Events]
  end

  M1[URS-01 Auth] --> EP
  PG --> M3[URS-03 Context Gate]
  EP --> M4[URS-04 Workflow / E-Sign]
  EP --> M5[URS-05 Authority]
  GE --> M6[URS-06 Audit Trail]
  PM --> M8[URS-08 Tenant]
  PG --> M12[URS-12 Doc Control]
  PG --> M14[URS-14 Complaints]
  PG --> M16[URS-16 Deviations]
  PG --> M18[URS-18 CAPA]
  PG --> M22[URS-22 Inspection]
  PG --> M23[URS-23 Batch Records]
  PG --> M27[URS-27 Reg Submissions]
  PG --> M30[URS-30 Notifications]
  M30 -.notifies.-> M2
  M35[URS-35 Backup] -.preserves.-> GE
```

| Source / target | Direction | Trigger | Data sent / received | Failure handling | Audit |
|---|---|---|---|---|---|
| URS-01 → URS-02 | upstream | session bootstrap | `userId`, `tenantId`, base role | URS-01 fails closed if token invalid | none here |
| URS-02 → URS-04 | downstream | regulated-action gating | effective permissions | URS-04 fails closed | URS-04 emits its own event |
| URS-02 → URS-05 | peer | regulated-decision authorisation | base role for SoD evaluation; role-change events for claims-version increment | URS-05 fails closed | mirrored events |
| URS-02 → URS-06 | downstream | every governance event | hash-chained event payload | rollback on audit failure | event |
| URS-02 → URS-08 | upstream / peer | tenant lifecycle | active tenant gate; default matrix seed on tenant creation | seed failure rolls back tenant creation | both |
| URS-02 → URS-30 | downstream | governance notifications | event payload | retry per URS-30 | event |

### 7.2 Change-Impact Matrix

| # | Module 2 artefact | Class | Affected modules | Specific impact | Required action | Re-validation |
|---|---|---|---|---|---|---|
| CIM-01 | Base-role enum | 1 | every regulated module | Adding or removing a base role breaks every consumer's matrix expectations | Migrate matrix; update shared constant; align CHECK constraint; full regression | Full IQ + OQ on every consumer + PQ |
| CIM-02 | Permission resource taxonomy | 1 (rename / remove) / 3 (add) | every consumer that gates on the resource | Removed resource breaks every gate; added resource has no effect until consumers opt in | Update taxonomy registry; align consumers; deprecation window | OQ on registry; PQ where consumers opt in |
| CIM-03 | Permission action taxonomy | 1 / 3 | as CIM-02 | as CIM-02 | as CIM-02 | as CIM-02 |
| CIM-04 | Permission matrix shape | 1 | every consumer | Schema change forces consumer migration | Schema migration with idempotency guards; align consumers | IQ + OQ + PQ |
| CIM-05 | Effective-permissions endpoint shape | 1 | every front-end consumer | Front-end gates fail closed | Update shared types; align front ends | OQ + PQ |
| CIM-06 | Mode A admin invariant policy | 2 | every consumer that assumes admin permissions are immutable | Switching from Mode A weakens control posture | Document the change; QA + RA + Security approval | OQ + PQ + Penetration |
| CIM-07 | Self-modification block | 2 | every administrative path | Disabling weakens control posture | Document the change | OQ |
| CIM-08 | Governance event vocabulary | 3 (add) / 1 (rename) | URS-06, URS-30, URS-22 | Audit replay incomplete; notification rules silent | Update writers; align consumers | OQ + PQ |
| CIM-09 | Invitation TTL | 4 | URS-30 | Operational impact only | Update policy | none |
| CIM-10 | Bulk-operation envelope | 3 | URS-06 | Bulk correlation may be missing | Update consumers | OQ |
| CIM-11 | Removal-fallback policy | 2 | URS-22 (regulated reference graph) | Fallback may not fire if graph misconfigured | Update consumers | OQ + PQ |
| CIM-12 | Direct administrative provisioning gate | 1 | URS-22, URS-08 | Loosening to tenant administrators is an inspector-visible weakening | Document; founder e-sign | Penetration + PQ |
| CIM-13 | Inline role-check exemption list | 2 | every consumer | New exemptions weaken matrix authority | Document each exemption with justification | OQ |
| CIM-14 | Periodic access review schedule | 4 | URS-30, URS-06 | Operational cadence change | Update tenant policy | none |
| CIM-15 | Role catalogue label / description | 4 | none functional | UX-only impact | Update registry | none |

### 7.3 Change classification and re-validation triggers

| Class | Definition | Examples | Required gates | Default re-validation |
|---|---|---|---|---|
| 1 — Substrate-breaking | Schema, contract, or semantic change that can break consumers | CIM-01, 02 (rename / remove), 03 (rename / remove), 04, 05, 12 | URS-13 approval by QA, RA, Security, Platform; founder e-signature when authority semantics are touched | Full IQ + OQ + PQ |
| 2 — Semantic-shift | Behaviour change without contract change | CIM-06, 07, 11, 13 | URS-13 approval by QA, RA, Security; differential test report archived | OQ + PQ |
| 3 — Additive | Pure addition not changing existing consumer contracts | CIM-02 (add), 03 (add), 08 (add), 10 | URS-13 approval by QA + RA | OQ |
| 4 — Configuration | Tenant-level value change | CIM-09, 14, 15 | Tenant-administrator electronic signature | none |

---

## 8. AI / Automation / Human-in-the-Loop Controls

Module 2 contains no AI. The matrix resolver is deterministic. The role-catalogue, permission-resource, and permission-action registries are static within a release. The access-governance lifecycle is rule-based. No large-language model is invoked anywhere on the access-governance path.

EU AI Act (Regulation (EU) 2024/1689) Article 3(1) applicability: not applicable to this module; documented exclusion appears in §14.

The MIRA AI agent (URS-32) does not advise on access governance, role-catalogue, or matrix decisions. The MIRA prompt envelope echoes effective permissions for downstream regulated-task gating only; it never alters access state in this module.

The human-in-the-loop pattern in this module covers every administrative governance action (invite, role change, deactivate, reactivate, remove, force password reset, matrix edit, administrative provisioning, governance export). Every such action routes through the Controlled Approval Modal and electronic signature.

---

## 9. Reports, Dashboards, and Exports

| Report | Filters | Columns | Access | Format | Audit | Retention |
|---|---|---|---|---|---|---|
| Role-assignment register | tenant, role, status | user, role, since, last active | administrator / auditor | CSV | yes (export) | seven years |
| Access-governance events | range, tenant, actor, target, event | full row | administrator / auditor | CSV / PDF / JSONL | yes | seven years |
| Permission-matrix history | tenant, role, resource, action, range | matrix_version, before, after, actor | administrator / auditor | CSV | yes | retain all versions |
| Permission coverage | tenant | role × resource × action grid | administrator / auditor | CSV | yes | seven years |
| Dormant users | tenant, threshold (e.g., ninety days no login) | user, last login, role | administrator | CSV | yes | one year |
| Pending invitations | tenant, status | invitee, inviter, role, sent at, expires at | administrator | CSV | yes | one year |
| Self-modification rejection log | range | actor, target, attempted action | administrator / auditor | CSV | yes | seven years |
| Mode A admin invariant rejection log | range | actor, attempted edit | administrator / auditor | CSV | yes | seven years |
| Bulk-operation register | range | bulk operation id, actor, operation, count, reason | administrator / auditor | CSV | yes | seven years |
| Periodic access review attestation | review window | reviewer, signed at, scope, exceptions | administrator / auditor | PDF | yes (export + signature) | twenty-five years |

Every export routes through the Controlled Approval Modal. Download URLs are signed and time-to-live-limited to fifteen minutes.

---

## 10. Notifications and Queues

| Trigger | Channel | Recipient | Priority | Retry | Suppression | Audit |
|---|---|---|---|---|---|---|
| Invitation issued | e-mail | invitee | normal | three with backoff | one per fifteen minutes per e-mail | yes |
| Invitation expiring (T-1 day) | e-mail | invitee | normal | three | one per day per invitation | yes |
| Invitation expired | e-mail | inviter | normal | three | one per day per invitation | yes |
| Role assigned | e-mail | affected user | normal | three | one per minute per user | yes |
| Role downgraded | e-mail | affected user | high | three | one per five minutes per user | yes |
| Deactivation | e-mail | affected user | high | three | one per five minutes per user | yes |
| Reactivation | e-mail | affected user | normal | three | one per five minutes per user | yes |
| Removal | e-mail | affected user where deliverable | high | three | one per fifteen minutes per user | yes |
| Force password reset | e-mail | affected user | high | three | one per five minutes per user | yes |
| Permission matrix edited (digest) | e-mail | tenant administrator queue | normal | three | one digest per hour per tenant | yes |
| Administrative provisioning | e-mail | tenant administrator queue + provisioned user | high | three | one per fifteen minutes per tenant | yes |
| Periodic access review window opens | e-mail | tenant administrator queue | normal | three | one per day per tenant | yes |
| Dormant-user threshold crossed | e-mail | tenant administrator queue | normal | three | one per day per user | yes |

Queues:

- **Pending invitations (administrator).**
- **Pending review for matrix diffs (administrator).**
- **Periodic access review window (administrator).**
- **Bulk-operation results pending review (administrator).**
- **Self-modification / Mode A rejection alerts (security operations centre).**

---

## 11. Error Handling and Negative Paths

### 11.1 Error envelope

All error responses follow the envelope: human message, machine-readable code in upper-snake-case, optional structured details, correlation identifier.

### 11.2 Error-code catalogue

| Code | HTTP | Path | UI behaviour |
|---|---|---|---|
| INVALID_ROLE | 400 | invite / role change / provision | inline field error |
| INVALID_RESOURCE | 400 | matrix edit | inline field error |
| INVALID_ACTION | 400 | matrix edit | inline field error |
| INVITE_DUPLICATE | 409 | invite | inline error naming the existing pending invitation |
| INVITE_TOKEN_INVALID | 400 | accept invite (URS-01) | "Your invitation is no longer valid." |
| ADMIN_IMMUTABLE | 400 | matrix edit on `admin` row | inline error; row already disabled in editor |
| SELF_ROLE_MODIFICATION_FORBIDDEN | 403 | any self-targeted change | inline error with rule citation |
| ADMIN_PROVISIONING_REQUIRED | 403 | direct provisioning by non-platform | inline error |
| USER_NOT_FOUND | 404 | per-user actions | inline error / redirect to user list |
| USER_ALREADY_ACTIVE | 409 | reactivate | inline error |
| USER_HAS_REGULATED_REFERENCES | 409 | remove (precondition) | UI offers fallback to deactivation |
| STALE_VERSION | 409 | matrix edit with stale matrix version | banner offers refresh + diff preservation |
| PERMISSION_DENIED | 403 | regulated administrative path | inline error |
| RBAC_DENIED | 403 | back-end stray permission denial | toast with correlation identifier; SOC alert if pattern |
| AUDIT_TRAIL_WRITE_FAILED | 500 | any state-changing action | toast; the originating action did NOT commit |
| BULK_LIMIT_EXCEEDED | 400 | bulk-operation submission with more than two hundred users | inline error; UI offers automatic split into ≤ 200-user batches |
| IMMUTABLE_FIELD_REJECTED | 400 | role-catalogue PATCH attempting to mutate `key` or `hierarchy_order` | inline error citing the immutable field |
| ROLE_KEY_UNKNOWN | 404 | role-catalogue PATCH on a non-existent key | inline error |
| RESOURCE_KEY_DUPLICATE | 409 | platform-administrator POST `/access/permissions/resources` with an existing key | inline error |
| ACTION_KEY_DUPLICATE | 409 | platform-administrator POST `/access/permissions/actions` with an existing key | inline error |
| INVALID_EMAIL | 400 | invitation / direct provisioning with malformed e-mail | inline field error |
| ESIG_REQUIRED | 422 | regulated administrative endpoint called without an active electronic-signature session | inline error in modal; UI re-opens the Controlled Approval Modal |
| TENANT_INACTIVE | 403 | any Module 2 endpoint when the tenant `status != 'active'` (URS-08) | redirect to login with a tenant-status flash |
| INVITE_EXPIRED | 410 | accept-invite path on a token whose `expires_at` has elapsed | "Your invitation is no longer valid. Contact your administrator." (the audit subset records `INVITE_EXPIRED` separately as a status-flip event) |
| INVITE_DUPLICATE_REJECTED | 409 | a second invitation submitted while the first is still active | inline error naming the existing pending invitation; this is the user-facing form of the audit event of the same name |
| PLATFORM_TENANT_ACCESS_DENIED | 403 | platform-identity attempts a tenant-scoped Module 2 action without a controlled support / break-glass envelope | inline error; SOC alert |

### 11.3 Negative-path catalogue

| Scenario | Detection | Response | UI behaviour |
|---|---|---|---|
| Self-targeted role change | back end | 403 `SELF_ROLE_MODIFICATION_FORBIDDEN` | inline error |
| Mode A admin row mutation | back end | 400 `ADMIN_IMMUTABLE` | inline error; cell pre-disabled |
| Invalid role enum | back end | 400 `INVALID_ROLE` | inline error |
| Invalid resource | back end | 400 `INVALID_RESOURCE` | inline error |
| Invalid action | back end | 400 `INVALID_ACTION` | inline error |
| Concurrent matrix edit (stale version) | back end | 409 `STALE_VERSION` | banner; preserve diff |
| Duplicate invitation | back end | 409 `INVITE_DUPLICATE` | inline error |
| Removal of user with regulated references | back end | 409 `USER_HAS_REGULATED_REFERENCES` | offer fallback to deactivation |
| Direct provisioning by non-platform actor | back end | 403 `ADMIN_PROVISIONING_REQUIRED` | inline error |
| Invitation expired | scheduled job | event `INVITE_EXPIRED`; status flipped | listing reflects status |
| Audit-log write failure | back end | 500 `AUDIT_TRAIL_WRITE_FAILED` | rollback; toast |
| Bulk-operation partial failure | back end | 207 multi-status with per-row outcome | per-row badges; UI groups failed rows |
| Permission denied at front-end guard | front end | none (no API call) | render access-denied state |
| Permission denied at back-end hook | back end | 403 `PERMISSION_DENIED` / `RBAC_DENIED` | toast |
| Tenant inactive | URS-08 → URS-01 → URS-02 | 403 `TENANT_INACTIVE` | redirect to login |
| Invitation send failure | URS-30 | non-blocking warning | toast offering manual resend |

---

## 12. Security, Privacy, and Tenant Isolation

### 12.1 Authentication dependency

URS-02 is reached only through an authenticated session established by URS-01. The matrix resolver fails closed if the session lacks `tenantId`.

### 12.2 Authorisation pipeline

`authenticate hook → tenant hook → rbac hook → context gate hook → workflow hook → authorityCheckHook`. URS-02's RBAC hook performs the matrix lookup; deny-by-default semantics apply.

### 12.3 Tenant isolation

Every database operation routes through the Tenant Data Access Layer with tenant context bound to the connection. Every Module 2 table has Row-Level Security enabled in the same migration that creates it. Cross-tenant operations route through the explicit platform-context API only where documented.

### 12.4 Encryption

At rest: invitation token hashed; matrix rows protected by RLS; KMS keys for any export encryption. In transit: TLS 1.2 or higher with HTTP Strict Transport Security preload.

### 12.5 Logging hygiene

Logs scrub invitation tokens and any field tagged sensitive. Structured logs carry the correlation identifier on every request.

### 12.6 Privacy and data residency

E-mail addresses processed under the tenant's data-residency contract. Invitation messages may contain tenant-confidential data and are excluded from public exports.

### 12.7 Periodic access review

Per URS-01 §12.10, the access-review window is at least quarterly for GxP-critical tenants and at least semi-annually for non-critical tenants. Module 2 surfaces the review queue on `/admin/governance` with the "Active assignments" filter and electronically signs each completed review.

### 12.8 Periodic audit-trail review

Per URS-01 §12.11, high-risk governance events (`ROLE_DOWNGRADED`, `ADMINISTRATIVE_PROVISIONING`, `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`, `RBAC_DENIED` clusters) are reviewed within one business day; routine events at least monthly. The triage decision is electronically signed and persisted into the audit log.

### 12.9 Security-operations alert thresholds

The Security Operations Centre receives alerts on the following module-2 patterns. Alert routing channel is e-mail plus the SOC chat workspace; on-call engineer acknowledges within one business day for high-severity alerts.

| Pattern | Threshold | Severity | Channel |
|---|---|---|---|
| `RBAC_DENIED` cluster (same user) | ten events per user per minute | critical | SOC chat + on-call e-mail |
| `RBAC_DENIED` cluster (same tenant) | one hundred events per tenant per minute | critical | SOC chat + on-call e-mail |
| `SELF_ROLE_MODIFICATION_FORBIDDEN` rejection | any single event | informational (digest hourly) | SOC e-mail digest |
| `ADMIN_IMMUTABLE` rejection | any single event | informational (digest hourly) | SOC e-mail digest |
| `ADMINISTRATIVE_PROVISIONING` | any single event | informational (real-time) | SOC chat |
| `USER_REMOVAL_FALLBACK_TO_DEACTIVATION` | any single event | informational (digest hourly) | SOC e-mail digest |
| `MATRIX_VERSION_INCREMENTED` rate spike | five times the rolling-30-day median in a one-hour window | high | SOC chat |
| Failed `AUDIT_TRAIL_WRITE_FAILED` on a module-2 endpoint | any single event | critical | SOC chat + on-call page |

### 12.10 Self-modification block — scope

The self-modification block (BR-02-03) covers, for any user-invoked path where `actor.user_id == target.user_id`: role change, deactivate, reactivate, remove, force password reset, matrix mutation on rows where `target.user_id` is the actor's own membership, and direct administrative provisioning of self. The block does NOT cover the user's own profile metadata (display name, avatar, time-zone preference), MFA enrolment lifecycle, or password change by the user themselves; those paths are owned by URS-01 and are intentionally self-service.

### 12.11 Secure export

Every export routes through the Controlled Approval Modal. Signed download URLs with fifteen-minute time-to-live. Integrity manifest accompanies every export.

---

## 13. Data Integrity and ALCOA+ Controls

| Principle | Module 2 control | Requirement | Verification |
|---|---|---|---|
| Attributable | Every governance event carries `actor_user_id` and `tenant_id`; `'system'` literal forbidden as actor for user-invoked routes (BR-02-13) | URS-02-AUD-001 | Integration test: every state mutation produces an audit row with non-null actor identifier |
| Legible | Audit rows store structured JSON metadata; exports render in PDF and machine-readable formats | URS-02-REP-001 | Export test: PDF readable; CSV imports without ambiguity |
| Contemporaneous | All timestamps from server clock; client-supplied timestamps dropped | URS-02-AUD-002 | Integration test |
| Original | Append-only access-governance event subset; soft-delete of memberships; versioned policy registries | URS-02-AUD-003 | Validation test: append-only enforcement |
| Accurate | Zod validation at every boundary; back-end re-validation; unique constraints; SoD checks | URS-02-DATA-001 | Integration test |
| Complete | Every event in the vocabulary has at least one writer | URS-02-AUD-004 | Validation test |
| Consistent | Matrix-version monotonic; transactional mutation + audit + version increment | URS-02-AUD-005 | Concurrency test |
| Enduring | Retention policies (seven years governance; twenty-five years authority change log mirror) | URS-02-DATA-002 | Migration test |
| Available | Read paths administrator-accessible; export self-service | URS-02-REP-002 | End-to-end test |

---

## 14. Regulatory Mapping

| Identifier | Control | Regulation / Guidance | Clause | Applicable | Implementation expectation |
|---|---|---|---|---|---|
| RG-02-001 | Limit access to authorised individuals | 21 CFR Part 11 | §11.10(d) | Yes | Permission matrix + RBAC hook + `PermissionGuard` + RLS |
| RG-02-002 | System-administrator authority | 21 CFR Part 11 | §11.300 | Yes | Force password reset; deactivation; reactivation |
| RG-02-003 | Audit trail of operator actions | 21 CFR Part 11 | §11.10(e) | Yes | Hash-chained governance events |
| RG-02-004 | Logical access controls | EU GMP Annex 11 | §12, §12.1 | Yes | Per-tenant matrix; least privilege; deny-by-default |
| RG-02-005 | Periodic review of computerised systems | EU GMP Annex 11 | §11 | Yes | Periodic access review per §12.7 |
| RG-02-006 | Validation of computerised systems | EU GMP Annex 11 | §4 | Yes | CSV / CSA pack per §17 |
| RG-02-007 | Documentation and record integrity | EU GMP Chapter 4 | applicable | Yes | Versioning + retention |
| RG-02-008 | Risk-based assurance | FDA Computer Software Assurance for Production and Quality Management System Software, Final Guidance, February 2026 | applicable | Yes | Risk classification per validation pack |
| RG-02-009 | ALCOA+ data integrity | MHRA Data Integrity Guidance (2018) | nine principles | Yes | §13 mapping |
| RG-02-010 | Records retention | EU GMP Annex 11 | §17 | Yes | Seven years governance; twenty-five years authority mirror |
| RG-02-011 | Privacy and data-subject rights | GDPR; DPDP Act (India) | applicable | Conditional | Tenant-scoped processing; export-own-data |
| RG-02-012 | Identity-management standards | ISO/IEC 27001 | A.5.16, A.8.5 | Yes | Role catalogue + matrix + access review |
| RG-02-013 | EU AI Act applicability | Regulation (EU) 2024/1689 | Article 3(1) | Not applicable to this module | No AI; documented exclusion |
| RG-02-014 | India regulatory framework (CDSCO; D&C Act 1940; CDSCO GCP; DPDP Act, 2023) | Per applicability | Conditional per tenant operation | Conditional | Tenant onboarding captures the operating jurisdictions and the RA disposition signed by the Regulatory Affairs Head before tenant activation; the launch URS does not assert specific clause numbers — the disposition is the controlling artefact and is filed under the tenant's onboarding pack maintained by URS-08 |
| RG-02-015 | Notifications integrity | Internal | not applicable | Yes | URS-30 substrate |

### 14.1 Predicate-rule applicability matrix

| Record / signature | Predicate-rule basis | Part 11 applicable? | Retention | Owner | Evidence |
|---|---|---|---|---|---|
| Electronic signature for invite, role change, deactivate, reactivate, remove, force password reset, matrix edit, administrative provisioning, governance export | GxP access-control approval (21 CFR Part 11 §11.10(d), §11.10(g); EU GMP Annex 11 §12, §14) | Yes | seven years | QA / Validation | Electronic-signature row + linked audit row + governance export |
| Permission matrix row | Access-control configuration evidence (21 CFR Part 11 §11.10(d); EU GMP Annex 11 §12) | Yes | retain all versions | QA / Information Security | Matrix-history report + audit row |
| Role-catalogue version | Access-control configuration baseline (EU GMP Annex 11 §12) | Yes | retain history | QA / Information Security | Catalogue version history + audit row |
| Permission-resource and permission-action taxonomy registries | Access-control configuration baseline | Yes | retain history | QA / Information Security | Taxonomy version history |
| Membership row | Access evidence | Yes | seven years | QA | Audit export |
| Invitation row | Operational record evidencing invitation lifecycle | Conditional — the audit row of the invitation IS predicate evidence; the row itself supports the audit | seven years; expired tokens hashed only | Information Security | Invitation lifecycle test |
| Bulk-operation row | Correlation evidence | Yes (when correlated to regulated mutations) | seven years | QA | Bulk-operation register |
| Periodic access review attestation | Compliance evidence (EU GMP Annex 11 §11) | Yes | twenty-five years | QA | Signed attestation + audit row |
| Self-modification / Mode A rejection row | Forensic security evidence | Conditional — the audit row is predicate evidence for access-control reviews | seven years | Information Security | Rejection log |

The predicate-rule applicability matrix is a controlled artefact; updating it is a Class 1 change per §7.

---

## 15. URS Requirements Register

### 15.1 Front-end (FE)

- URS-02-FE-001 — `RoleGuard` MUST gate every administrative route in §5.1 by base role tier. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-FE-002 — `PermissionGuard` MUST gate every regulated administrative button by `(resource, action)` pair from the effective permissions returned by `/access/me/permissions`. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-FE-003 — Permission matrix editor MUST visually disable all `admin` rows (Mode A invariant) and MUST NOT permit submission of an `admin`-row diff. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-FE-004 — User-detail action menu MUST disable self-targeted actions (role change, deactivate, remove, force password reset) and surface the rule citation in the tooltip. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-FE-005 — Permission matrix editor MUST detect a stale matrix version and surface a banner with diff preservation. Priority MUST. Risk MEDIUM. Maps RG-02-004.
- URS-02-FE-006 — Every regulated administrative action MUST route through the Controlled Approval Modal; meaning of signature and reason are mandatory. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-FE-007 — Front end MUST NOT include `ip` / `userAgent` / `timestamp` / `performedBy` in any request body. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-FE-008 — Bulk-action confirmation modal MUST name the count of affected users and the cascade. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-FE-009 — User list, user detail, governance report, and matrix editor MUST implement loading, error, success, empty, permission-restricted, and audit-visibility states. Priority MUST. Risk MEDIUM. Maps RG-02-004.
- URS-02-FE-010 — Every route in §5.1 MUST be registered in the application router before release. Priority MUST. Risk LOW. Maps RG-02-004.
- URS-02-FE-011 — Removal action surfaces a fallback-to-deactivation confirmation when the back end returns `USER_HAS_REGULATED_REFERENCES`. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-FE-012 — Direct administrative provisioning UI is hidden from non-platform identities; the route is enforced by `RoleGuard`. Priority MUST. Risk HIGH. Maps RG-02-001.

### 15.2 Back-end (BE)

- URS-02-BE-001 — `GET /access/me/permissions` MUST return `{baseRole, effectivePermissions, tenantId, matrixVersion, claimsVersion}` atomically. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-BE-002 — Mode A admin invariant: any matrix mutation targeting `(role='admin', …)` MUST be rejected with `ADMIN_IMMUTABLE`. Priority MUST. Risk CRITICAL. Maps RG-02-001.
- URS-02-BE-003 — Self-modification block: any user-invoked mutation where `actor.user_id == target.user_id` for role / membership / matrix changes MUST be rejected with `SELF_ROLE_MODIFICATION_FORBIDDEN`. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-BE-004 — Permission resource and action MUST exist in the taxonomy registry at the moment of mutation; rejections MUST emit `INVALID_RESOURCE` / `INVALID_ACTION`. Priority MUST. Risk HIGH. Maps RG-02-004.
- URS-02-BE-005 — Permission-matrix mutation MUST be idempotent and atomic with the matrix-version increment in the same transaction. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-BE-006 — Role change MUST cascade-revoke all sessions of the affected user and increment claims version in the same transaction. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-BE-007 — Role downgrade MUST emit `ROLE_DOWNGRADED` to the URS-01 authority change log under the actor's identity. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-BE-008 — Direct administrative provisioning MUST require the actor to hold `platform_admin` or `super_admin` identity; tenant administrators MUST receive `ADMIN_PROVISIONING_REQUIRED`. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-BE-009 — Removal MUST consult the regulated-record reference graph; if any reference exists, fall back to deactivation with `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-BE-010 — Invitation MUST be unique-active per `(tenant_id, lower(email))`; second attempt rejected with `INVITE_DUPLICATE`. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-BE-011 — Every regulated administrative endpoint MUST require electronic signature; metadata is server-derived (BR-02-12). Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-BE-012 — Audit-log write failure MUST roll back the originating action (BR-02-13). Priority MUST. Risk CRITICAL. Maps RG-02-003.
- URS-02-BE-013 — Every **tenant-scoped** Module 2 table MUST have Row-Level Security enabled in the same migration that creates it. Global / platform-context registries (`role_catalogue`, `permission_resources`, `permission_actions`) MUST instead carry explicit platform-context access policies, version history, audit logging on every mutation, and **no tenant-user write path** (tenant identities may only read). Priority MUST. Risk HIGH. Maps RG-02-004.
- URS-02-BE-014 — Inline string-equality role checks outside the documented exemption list MUST be removed; static-analysis gate enforces. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-BE-015 — Effective-permissions resolver MUST be deterministic and side-effect-free; cache key includes matrix version and claims version. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-BE-016 — Bulk-operation envelope MUST share `bulkOperationId` across all per-row audit events. Priority MUST. Risk LOW. Maps RG-02-003.

### 15.3 Workflow (WF)

- URS-02-WF-001 — Membership state machine adheres to §5.4 transitions. Priority MUST. Risk MEDIUM. Maps RG-02-004.
- URS-02-WF-002 — Invitation auto-expires at seven days; expired status emits `INVITE_EXPIRED`. Priority MUST. Risk LOW. Maps RG-02-004.
- URS-02-WF-003 — Periodic access review reminders fire thirty days before each window opens (per URS-30). Priority MUST. Risk MEDIUM. Maps RG-02-005.

### 15.4 Data (DATA)

- URS-02-DATA-001 — Every entity in §6.1 exists with the columns and constraints in §6.2; uniqueness enforced at the database level. Priority MUST. Risk HIGH. Maps RG-02-004.
- URS-02-DATA-002 — `permission_matrix` retains all versions through the audit log; matrix-history report reconstructs any past tenant state. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-DATA-003 — Soft-deleted memberships remain queryable for audit / export paths. Priority MUST. Risk MEDIUM. Maps RG-02-009.
- URS-02-DATA-004 — Versioned policy registries (role catalogue, permission resources, permission actions) retain history accessible by version and effective-from. Priority MUST. Risk MEDIUM. Maps RG-02-007.

### 15.5 Security (SEC)

- URS-02-SEC-001 — All requests route through Tenant Data Access Layer. Priority MUST. Risk CRITICAL. Maps RG-02-004.
- URS-02-SEC-002 — Periodic access review per URS-01 §12.10 MUST be performed on Module 2 governance events; outcomes electronically signed; retained twenty-five years. Priority MUST. Risk HIGH. Maps RG-02-005.
- URS-02-SEC-003 — Periodic audit-trail review per URS-01 §12.11 MUST cover access-governance events; high-risk events triaged within one business day. Priority MUST. Risk HIGH. Maps RG-02-003, RG-02-005.
- URS-02-SEC-004 — Logs scrub invitation tokens and any field tagged sensitive. Priority MUST. Risk MEDIUM. Maps RG-02-011.

### 15.6 Audit (AUD)

- URS-02-AUD-001 — Every state-changing action emits an audit event with all required fields per URS-06 §6.6. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-AUD-002 — Server-derived metadata is the only source of truth for audit fields. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-AUD-003 — Audit-log write failure rolls back the originating action. Priority MUST. Risk CRITICAL. Maps RG-02-003.
- URS-02-AUD-004 — All access-governance event codes in §6.6 MUST have at least one writer. Priority MUST. Risk MEDIUM. Maps RG-02-003.
- URS-02-AUD-005 — `RBAC_DENIED` MUST be written for stray API calls that escape the front-end guard. Priority MUST. Risk MEDIUM. Maps RG-02-003.

### 15.7 Electronic Signature (ESIG)

- URS-02-ESIG-001 — Every regulated administrative action is electronically signed. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-ESIG-002 — Signature row carries server-derived `ip`, `user_agent`, `signed_at`, `signed_by`. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-ESIG-003 — Signature is linked to the action row; subsequent change to the signed entity invalidates the signature. Priority MUST. Risk MEDIUM. Maps RG-02-003.
- URS-02-ESIG-004 — Re-authentication is mandatory; multi-factor step-up is required for matrix edits and administrative provisioning. Priority MUST. Risk MEDIUM. Maps RG-02-003.

### 15.8 AI / Human-in-the-Loop (AI)

- URS-02-AI-001 — No AI is invoked in any access-governance decision path. Priority MUST. Risk HIGH. Maps RG-02-013.
- URS-02-AI-002 — When the MIRA agent is invoked from other modules, it MUST receive effective permissions but MUST NOT mutate access state. Priority MUST. Risk MEDIUM. Maps RG-02-013.

### 15.9 Integration (INT)

- URS-02-INT-001 — URS-01 supplies authenticated identity and tenant; Module 2 fails closed on missing tenant. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-INT-002 — URS-04 receives effective permissions and base role for HITL / e-signature gating. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-INT-003 — URS-05 receives base role for SoD evaluation; role-change events trigger URS-05 claims-version increment. Priority MUST. Risk HIGH. Maps RG-02-001.
- URS-02-INT-004 — URS-06 ingests every governance event and preserves hash-chain integrity. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-INT-005 — URS-08 active-tenant gate rejects operations on inactive tenants. Priority MUST. Risk MEDIUM. Maps RG-02-001.
- URS-02-INT-006 — URS-30 delivers all notifications listed in §10. Priority MUST. Risk LOW. Maps RG-02-015.
- URS-02-INT-007 — URS-35 preserves audit-chain integrity across restore for governance events. Priority MUST. Risk HIGH. Maps RG-02-003.

### 15.10 Reports / Exports (REP)

- URS-02-REP-001 — All reports in §9 are implemented with documented filters, columns, formats, and access rules. Priority MUST. Risk MEDIUM. Maps RG-02-004.
- URS-02-REP-002 — Governance-report export produces a self-verifying integrity manifest; exports are electronically signed. Priority MUST. Risk HIGH. Maps RG-02-003.
- URS-02-REP-003 — Export download URLs are signed and time-to-live limited (fifteen minutes). Priority MUST. Risk MEDIUM. Maps RG-02-011.

### 15.11 Validation Evidence (VAL)

- URS-02-VAL-001 — IQ pack verifies the schema of all migrations creating Module 2 tables; RLS enabled on every table. Priority MUST. Risk HIGH. Maps RG-02-006.
- URS-02-VAL-002 — OQ pack tests effective-permissions resolution, Mode A invariant, self-modification block, matrix-version atomicity, role-change cascade, removal fallback, direct administrative provisioning, governance event vocabulary, error-code emission, hash-chain integrity. Priority MUST. Risk HIGH. Maps RG-02-006.
- URS-02-VAL-003 — PQ pack runs end-to-end scenarios for invite, role change, deactivate, reactivate, remove (with fallback), force password reset, matrix edit, governance export, periodic access review. Priority MUST. Risk HIGH. Maps RG-02-006.
- URS-02-VAL-004 — Regression pack exercises every requirement in §15. Priority MUST. Risk MEDIUM. Maps RG-02-006.
- URS-02-VAL-005 — Traceability matrix links each requirement identifier in §15 to at least one IQ / OQ / PQ test case. Priority MUST. Risk HIGH. Maps RG-02-006.
- URS-02-VAL-006 — Supplier and service-provider qualification pack per §17.1 (notification provider, KMS, cloud hosting, backup-and-restore provider, SOC / SIEM). Priority MUST. Risk HIGH. Maps RG-02-006.
- URS-02-VAL-007 — Inspection-ready evidence index per §17.2 linking each URS requirement through FS, DS, source code, test protocol, executed evidence, deviations, release approval. Priority MUST. Risk HIGH. Maps RG-02-006, RG-02-010.

---

## 16. Acceptance Criteria and Test Cases

This section is read by three audiences: product owners and business validators (§16.1), engineering and test automation (§16.2), QA / validation engineers (§16.3 + §16.4).

### 16.1 Plain-language behaviour scenarios

#### TC-PLAIN-001 — Onboarding a new QA reviewer

A QA Manager invites Sarah as `reviewer`. Sarah receives an e-mail, accepts the invitation, sets her password, and lands as `reviewer` in the tenant. Her approve buttons are disabled until an Authority Profile is assigned by URS-05.

#### TC-PLAIN-002 — Promotion forces a fresh login

A QA Manager promotes Sarah from `reviewer` to `quality_lead`. Sarah's open browser tabs are kicked out within seconds; on next login she sees the broader set of permissions her new role grants.

#### TC-PLAIN-003 — Self-promotion blocked

A QA Manager attempts to widen their own permissions in the matrix. The system rejects with a clear message; nothing is mutated; the rejection is recorded for forensic review.

#### TC-PLAIN-004 — Cannot weaken admin

A platform administrator attempts to disable a permission for `admin`. The system rejects with a clear message and the matrix editor disables the row visually so the attempt is unreachable through the UI.

#### TC-PLAIN-005 — Removing a user with regulated history

A QA Manager attempts to remove an analyst who has authored a deviation. The system warns and offers fallback to deactivation; the analyst is deactivated and the deviation reference remains intact for audit traceability.

#### TC-PLAIN-006 — Concurrent matrix edits

Two administrators edit the matrix in parallel browser tabs. The second to save sees a banner "Matrix version updated by another administrator — refresh to continue" with their pending diffs preserved client-side.

#### TC-PLAIN-007 — Force password reset

A QA Manager forces Sarah to reset her password. Sarah's open sessions are revoked; her next login redirects her to the change-password screen and she cannot do anything regulated until the new password is set.

#### TC-PLAIN-008 — Inspector reads the access-governance report

An external auditor with `auditor` base role opens the governance report for the inspection window, filters to the role-change events in the period, and exports a PDF; the export is electronically signed and carries an integrity manifest.

#### TC-PLAIN-009 — Periodic access review

The QA Manager is reminded thirty days before the quarterly review window opens. She reviews every active assignment, signs the review attestation, and documents one exception (a stale assignment to a former employee); the system creates a CAPA per URS-01 §12.10.

#### TC-PLAIN-010 — Direct provisioning by a tenant administrator is denied

A tenant administrator attempts to bypass the invitation flow and POST directly to the provisioning endpoint. The system rejects with a clear message and the attempt is recorded; only platform administrators may directly provision.

#### TC-PLAIN-011 — Bulk role change

A QA Manager promotes seven `reviewer` users to `quality_lead` in one operation. Each user's sessions are revoked; each receives an e-mail; the audit log records seven `ROLE_ASSIGNED` events sharing one bulk-operation identifier with one electronic signature covering them all.

#### TC-PLAIN-012 — Permission denied at section level

Sarah (a `quality_lead`) opens a deviation page. The "Export deviations" button is disabled with tooltip "Permission denied — `deviations:export`" because the matrix says `quality_lead` does not have `deviations:export` in this tenant.

#### TC-PLAIN-013 — Invitation expires

An invitation sent on 1 August is not accepted by 8 August. The invitation is automatically marked expired; the inviter receives an e-mail; the inviter may issue a new invitation, which creates a fresh token while the expired row remains for audit.

### 16.2 Technical test cases

#### TC-TECH-001 — Effective-permissions endpoint returns canonical shape

- Type: Integration. Linked: URS-02-BE-001.
- Steps: Authenticate as a `quality_lead`; GET `/access/me/permissions`. Verify response carries `baseRole`, `effectivePermissions`, `tenantId`, `matrixVersion` ≥ 1, `claimsVersion` ≥ 1.

#### TC-TECH-002 — Mode A admin invariant

- Type: Integration. Linked: URS-02-BE-002, BR-02-02.
- Steps: As an `admin` holding `tenant_admin_authority`, PATCH `/access/permissions/matrix` with a row `(role='admin', resource='users', action='manage_users', is_allowed=false)`. Expect HTTP 400 `ADMIN_IMMUTABLE`; matrix unchanged; audit row recorded.

#### TC-TECH-003 — Self-modification block

- Type: Integration. Linked: URS-02-BE-003, BR-02-03.
- Steps: As an `admin` holding `tenant_admin_authority`, PATCH `/access/users/<self>/role`. Expect 403 `SELF_ROLE_MODIFICATION_FORBIDDEN`; membership unchanged; audit row recorded.

#### TC-TECH-004 — Idempotent matrix upsert without value change

- Type: Integration. Linked: URS-02-BE-005, BR-02-05.
- Steps: PATCH a matrix row with the same `is_allowed` value. Expect 200; no `PERMISSION_UPDATED` audit row; no matrix-version increment.

#### TC-TECH-005 — Matrix-version atomic increment

- Type: Concurrency. Linked: URS-02-BE-005, BR-02-06.
- Steps: Fire 50 parallel PATCH requests, each toggling distinct rows. After completion, sum of increments equals 50; matrix-version monotonic; one audit row per accepted mutation.

#### TC-TECH-006 — Role-change cascade

- Type: Integration. Linked: URS-02-BE-006, BR-02-07.
- Preconditions: User has two active sessions.
- Steps: PATCH `/access/users/<id>/role` with downgrade. Expect both sessions revoked; `ROLE_ASSIGNED`, `ROLE_DOWNGRADED`, `SESSIONS_REVOKED_DUE_TO_ACCESS_CHANGE` audit rows; claims version incremented.

#### TC-TECH-007 — Removal fallback

- Type: Integration. Linked: URS-02-BE-009, BR-02-10.
- Preconditions: User has authored at least one regulated record.
- Steps: DELETE `/access/users/<id>/remove`. Expect status 409 with offer to fall back; on confirmation, deactivation event chain emitted including `USER_REMOVAL_FALLBACK_TO_DEACTIVATION`.

#### TC-TECH-008 — Direct administrative provisioning gate

- Type: Integration. Linked: URS-02-BE-008, BR-02-11.
- Steps: As an `admin` holding `tenant_admin_authority`, POST `/access/users/provision`. Expect 403 `ADMIN_PROVISIONING_REQUIRED`. As `platform_admin`, repeat. Expect 201; `ADMINISTRATIVE_PROVISIONING` audit emitted.

#### TC-TECH-009 — Invitation duplicate

- Type: Integration. Linked: URS-02-BE-010.
- Steps: POST `/access/users/invite` for the same e-mail twice while the first invitation is still active. Expect 409 `INVITE_DUPLICATE`.

#### TC-TECH-010 — Audit-write failure rolls back

- Type: Chaos. Linked: URS-02-BE-012, URS-02-AUD-003.
- Steps: Force the audit log INSERT to fail mid-transaction. Expect 500 `AUDIT_TRAIL_WRITE_FAILED`; no membership row mutated; no electronic signature persisted.

#### TC-TECH-011 — Stale-version conflict

- Type: Integration. Linked: URS-02-BE-005.
- Steps: Open the matrix editor in two sessions; A saves; B saves with stale version. Expect 409 `STALE_VERSION`; B's diff preserved.

#### TC-TECH-012 — Inline role-check static analysis

- Type: Static analysis. Linked: URS-02-BE-014, BR-02-15.
- Steps: AST-walk all backend handlers for inline `request.user.role === 'admin'` patterns outside the documented exemption list. Expect zero matches.

#### TC-TECH-013 — Effective-permissions cache key

- Type: Integration. Linked: URS-02-BE-015, BR-02-14.
- Steps: Call `/access/me/permissions` twice without state change; verify identical response and cache hit. Mutate matrix; call again; verify cache invalidation.

#### TC-TECH-014 — Bulk operation correlation

- Type: Integration. Linked: URS-02-BE-016, BR-02-13.
- Steps: POST `/access/users/bulk/role` with 7 users. Verify 7 `ROLE_ASSIGNED` audit rows share one `bulkOperationId`; one electronic signature row references the bulk operation.

#### TC-TECH-015 — Cross-tenant isolation

- Type: Penetration. Linked: URS-02-SEC-001, URS-02-BE-013.
- Steps: As tenant A user, attempt to GET a user identifier known to live in tenant B. Expect 404 with no tenant B data leaked.

#### TC-TECH-016 — Periodic access review reminder

- Type: Integration. Linked: URS-02-WF-003, URS-02-SEC-002.
- Steps: Configure quarterly review; advance the clock to T-30 days. Verify reminder e-mail issued via URS-30.

#### TC-TECH-017 — Governance export integrity manifest

- Type: Integration. Linked: URS-02-REP-002.
- Steps: POST `/access/governance/report/export` with valid range. Verify signed download URL with TTL fifteen minutes; manifest includes start hash, end hash, row count, validation status; export electronically signed.

#### TC-TECH-018 — Migrations idempotent and create RLS-enabled tables

- Type: Migration. Linked: URS-02-VAL-001.
- Steps: Apply all Module 2 migrations to a clean database; apply them again. Verify schema, RLS enabled on every table, and seed of role catalogue / taxonomy / default tenant matrix.

#### TC-TECH-019 — Performance: effective permissions

- Type: Load. Linked: URS-02-BE-001.
- Steps: Sustain 500 RPS of `/access/me/permissions` for fifteen minutes. Verify p95 ≤ 100 ms; error rate < 0.1 %.

#### TC-TECH-020 — Performance: matrix edit

- Type: Load. Linked: URS-02-BE-005.
- Steps: Sustain 50 RPS of matrix-edit transactions. Verify p95 ≤ 300 ms; concurrent stale-version conflicts handled gracefully.

#### TC-TECH-021 — RBAC_DENIED forensic event

- Type: Integration. Linked: URS-02-AUD-005.
- Steps: Bypass the front-end guard (e.g., via direct API call from a test harness) and call a regulated administrative endpoint as a `viewer`. Verify 403 `PERMISSION_DENIED` and an `RBAC_DENIED` audit row.

#### TC-TECH-022 — Catalogue / taxonomy registry change

- Type: Integration. Linked: URS-02-DATA-004.
- Steps: As `platform_admin`, POST `/access/permissions/actions` for a new action key. Verify `PERMISSION_ACTION_ADDED` event; existing matrix unchanged; new action available in editor.

#### TC-TECH-023 — Invitation expiry job

- Type: Integration. Linked: URS-02-WF-002.
- Steps: Insert an invitation with `expires_at` in the past; trigger the expiry job. Verify status `expired`; `INVITE_EXPIRED` event; e-mail to inviter.

#### TC-TECH-024 — Three-layer guard hierarchy enforcement

- Type: Static analysis. Linked: URS-02-FE-002.
- Steps: AST-walk the front end for every regulated administrative button; verify each is wrapped — directly or transitively — by `RoleGuard` (admin tier) and `PermissionGuard`; for any approval / release / sign-off, additionally by `AuthorityGuard` (URS-01 component).

#### TC-TECH-025 — All access-governance event codes have a writer

- Type: Static analysis + Integration. Linked: URS-02-AUD-004.
- Steps: Static grep for each code in §6.6; run the regression suite; verify each code emitted at least once.

### 16.3 Acceptance criteria summary in Given / When / Then form

#### Functional

- AC-02-FUN-01 — Given a valid administrator invitation, When POST `/access/users/invite`, Then 201 with the invitation row and `INVITE_SENT` + `ROLE_ASSIGNED_PENDING` audit events.
- AC-02-FUN-02 — Given a role change, When committed, Then sessions revoked, claims version incremented, audit emitted, and the user must re-login.
- AC-02-FUN-03 — Given direct administrative provisioning by a `platform_admin`, When committed, Then user created with role and `ADMINISTRATIVE_PROVISIONING` event.

#### UI

- AC-02-UI-01 — Given the matrix editor, When viewing `admin` rows, Then they are visually disabled.
- AC-02-UI-02 — Given a stale matrix version, When attempting to save, Then a refresh banner appears and pending diffs are preserved.
- AC-02-UI-03 — Given a self-targeted action, When the user-detail page renders, Then the action is disabled with a rule-citation tooltip.

#### API

- AC-02-API-01 — Given an authenticated request to `/access/me/permissions`, When resolved, Then the response carries baseRole, effectivePermissions, tenantId, matrixVersion, claimsVersion.
- AC-02-API-02 — Given a matrix mutation that changes no values, When idempotently upserted, Then no audit row and no version increment.
- AC-02-API-03 — Given concurrent matrix edits, When the second hits a stale version, Then 409 `STALE_VERSION`.

#### Workflow

- AC-02-WF-01 — Given an unaccepted invitation at T+7 days, When the expiry job runs, Then status `expired` and `INVITE_EXPIRED` event.

#### Permission / Authority

- AC-02-PERM-01 — Given a non-platform actor, When POST `/access/users/provision`, Then 403 `ADMIN_PROVISIONING_REQUIRED`.
- AC-02-PERM-02 — Given a `viewer`, When the front end attempts a matrix edit, Then `RoleGuard` blocks at the route layer.
- AC-02-PERM-03 — Given a regulated administrative action, When submitted without electronic signature, Then 422 `ESIG_REQUIRED`.

#### Audit

- AC-02-AUD-01 — Given any governance mutation, When the audit transaction is rolled back, Then the mutation is also rolled back.
- AC-02-AUD-02 — Given a stray API call escaping the front-end guard, When denied by the back-end hook, Then 403 `PERMISSION_DENIED` and an `RBAC_DENIED` audit row.

#### Electronic signature

- AC-02-ESIG-01 — Given a permission-matrix edit, When submitted, Then signature row carries server-derived `ip`, `user_agent`, `signed_at`, `signed_by`.
- AC-02-ESIG-02 — Given a high-risk mutation (matrix edit, provisioning), When submitted, Then multi-factor step-up is required.

#### Data integrity

- AC-02-DI-01 — Given a soft-deleted membership, When administrator queries the audit-export endpoint, Then the membership appears in audit history.
- AC-02-DI-02 — Given concurrent matrix edits, When committed, Then matrix version strictly monotonic.

#### Integration

- AC-02-INT-01 — Given a role change, When committed, Then URS-01 revokes all sessions of the affected user.
- AC-02-INT-02 — Given URS-35 restores a database snapshot, When the integrity verifier runs, Then the audit chain validates end-to-end including governance events.

#### Report / export

- AC-02-REP-01 — Given an administrator exports the governance report, When the download completes, Then the manifest validates and the export is electronically signed.

#### AI / HITL

- AC-02-AI-01 — Static analysis finds zero references to large-language-model SDKs in Module 2.

#### Negative paths

- AC-02-NEG-01 — Given a duplicate invitation, When submitted, Then 409 `INVITE_DUPLICATE`.
- AC-02-NEG-02 — Given a removal of a user with regulated references, When submitted, Then 409 `USER_HAS_REGULATED_REFERENCES` with offered fallback.

#### Performance

- AC-02-PERF-01 — `/access/me/permissions` p95 ≤ 100 ms at 500 RPS sustained.
- AC-02-PERF-02 — Matrix-edit transaction p95 ≤ 300 ms at 50 RPS sustained.

#### Security

- AC-02-SEC-01 — Penetration test: cross-tenant token replay returns 404 with no tenant B data.
- AC-02-SEC-02 — Penetration test: bypass attempts of `RoleGuard` produce `RBAC_DENIED` audit rows.

#### Migration / backfill

- AC-02-MIG-01 — Module 2 migrations are idempotent.
- AC-02-MIG-02 — Applying all Module 2 migrations seeds the role catalogue and the default tenant matrix.

### 16.4 Requirements-to-test traceability

| Requirement | Plain-language | Technical | Given / When / Then |
|---|---|---|---|
| URS-02-FE-001 | TC-PLAIN-001 | TC-TECH-024 | AC-02-PERM-02 |
| URS-02-FE-002 | TC-PLAIN-012 | TC-TECH-024 | AC-02-PERM-02 |
| URS-02-FE-003 | TC-PLAIN-004 | TC-TECH-002 | AC-02-UI-01 |
| URS-02-FE-004 | TC-PLAIN-003 | TC-TECH-003 | AC-02-UI-03 |
| URS-02-FE-005 | TC-PLAIN-006 | TC-TECH-011 | AC-02-UI-02 |
| URS-02-FE-006 | TC-PLAIN-002 | TC-TECH-006, TC-TECH-014 | AC-02-ESIG-01 |
| URS-02-FE-007 | — | TC-TECH-014 | AC-02-ESIG-01 |
| URS-02-FE-008 | TC-PLAIN-011 | TC-TECH-014 | — |
| URS-02-FE-009 | — | TC-TECH-024 | — |
| URS-02-FE-010 | — | TC-TECH-018 | — |
| URS-02-FE-011 | TC-PLAIN-005 | TC-TECH-007 | AC-02-NEG-02 |
| URS-02-FE-012 | TC-PLAIN-010 | TC-TECH-008 | AC-02-PERM-01 |
| URS-02-BE-001 | — | TC-TECH-001, TC-TECH-013 | AC-02-API-01 |
| URS-02-BE-002 | TC-PLAIN-004 | TC-TECH-002 | AC-02-UI-01 |
| URS-02-BE-003 | TC-PLAIN-003 | TC-TECH-003 | — |
| URS-02-BE-004 | — | TC-TECH-022 | — |
| URS-02-BE-005 | TC-PLAIN-006 | TC-TECH-004, TC-TECH-005, TC-TECH-011 | AC-02-API-02, AC-02-API-03 |
| URS-02-BE-006 | TC-PLAIN-002 | TC-TECH-006 | AC-02-FUN-02, AC-02-INT-01 |
| URS-02-BE-007 | TC-PLAIN-002 | TC-TECH-006 | AC-02-AUD-01 |
| URS-02-BE-008 | TC-PLAIN-010 | TC-TECH-008 | AC-02-FUN-03, AC-02-PERM-01 |
| URS-02-BE-009 | TC-PLAIN-005 | TC-TECH-007 | AC-02-NEG-02 |
| URS-02-BE-010 | TC-PLAIN-013 | TC-TECH-009 | AC-02-NEG-01 |
| URS-02-BE-011 | — | TC-TECH-014 | AC-02-PERM-03 |
| URS-02-BE-012 | — | TC-TECH-010 | AC-02-AUD-01 |
| URS-02-BE-013 | — | TC-TECH-015, TC-TECH-018 | AC-02-SEC-01 |
| URS-02-BE-014 | — | TC-TECH-012 | — |
| URS-02-BE-015 | — | TC-TECH-013 | — |
| URS-02-BE-016 | TC-PLAIN-011 | TC-TECH-014 | — |
| URS-02-WF-001 | — | TC-TECH-006, TC-TECH-007 | — |
| URS-02-WF-002 | TC-PLAIN-013 | TC-TECH-023 | AC-02-WF-01 |
| URS-02-WF-003 | TC-PLAIN-009 | TC-TECH-016 | — |
| URS-02-DATA-001 | — | TC-TECH-018 | — |
| URS-02-DATA-002 | — | TC-TECH-005 | AC-02-DI-02 |
| URS-02-DATA-003 | — | (audit retention test) | AC-02-DI-01 |
| URS-02-DATA-004 | — | TC-TECH-022 | — |
| URS-02-SEC-001 | — | TC-TECH-015, TC-TECH-018 | AC-02-SEC-01 |
| URS-02-SEC-002 | TC-PLAIN-009 | TC-TECH-016 | — |
| URS-02-SEC-003 | — | (audit-trail review test) | — |
| URS-02-SEC-004 | — | (log scrub test) | — |
| URS-02-AUD-001 | — | TC-TECH-006, TC-TECH-014, TC-TECH-025 | — |
| URS-02-AUD-002 | — | TC-TECH-014 | AC-02-ESIG-01 |
| URS-02-AUD-003 | — | TC-TECH-010 | AC-02-AUD-01 |
| URS-02-AUD-004 | — | TC-TECH-025 | — |
| URS-02-AUD-005 | — | TC-TECH-021 | AC-02-AUD-02 |
| URS-02-ESIG-001 | — | TC-TECH-014 | AC-02-PERM-03 |
| URS-02-ESIG-002 | — | TC-TECH-014 | AC-02-ESIG-01 |
| URS-02-ESIG-003 | — | (signature invalidation test) | — |
| URS-02-ESIG-004 | — | TC-TECH-014 | AC-02-ESIG-02 |
| URS-02-AI-001 | — | (static analysis) | AC-02-AI-01 |
| URS-02-AI-002 | — | (integration) | — |
| URS-02-INT-001 | — | TC-TECH-001 | — |
| URS-02-INT-002 | — | TC-TECH-006 | — |
| URS-02-INT-003 | TC-PLAIN-002 | TC-TECH-006 | AC-02-INT-01 |
| URS-02-INT-004 | — | TC-TECH-025 | — |
| URS-02-INT-005 | — | (tenant inactive integration test) | — |
| URS-02-INT-006 | TC-PLAIN-001, TC-PLAIN-007 | TC-TECH-016 | — |
| URS-02-INT-007 | — | (DR drill) | AC-02-INT-02 |
| URS-02-REP-001 | TC-PLAIN-008 | TC-TECH-017 | AC-02-REP-01 |
| URS-02-REP-002 | TC-PLAIN-008 | TC-TECH-017 | AC-02-REP-01 |
| URS-02-REP-003 | TC-PLAIN-008 | TC-TECH-017 | — |
| URS-02-VAL-001 | — | TC-TECH-018 | — |
| URS-02-VAL-002 | All applicable | All applicable | All applicable |
| URS-02-VAL-003 | All applicable | All applicable | All applicable |
| URS-02-VAL-004 | — | The full TC-TECH suite re-run | — |
| URS-02-VAL-005 | — | This table is the seed of the traceability matrix | — |
| URS-02-VAL-006 | — | (supplier qualification evidence) | — |
| URS-02-VAL-007 | — | (evidence index) | — |

---

## 17. Validation and CSV/CSA Evidence Expectations

| Item | Required evidence |
|---|---|
| URS traceability | Matrix linking every requirement identifier in §15 to test-case identifiers and to a regulatory-mapping row in §14 |
| Risk assessment | GAMP 5 risk register; risk-based assurance level per FDA CSA |
| Configuration specification | Documented configuration of role-catalogue seed, default tenant matrix seed, taxonomy registry seed, and Mode A invariant policy |
| Functional specification | Matches §6 of this document |
| Design specification | Matches §6.1–§6.4 |
| Test protocols | IQ (schema, RLS, indexes, taxonomy / catalogue seed); OQ per URS-02-VAL-002; PQ per URS-02-VAL-003; regression per URS-02-VAL-004 |
| Test evidence | Pass / fail records per protocol step, traced to requirement identifier |
| Defect log | Defects mapped to URS requirement identifiers; resolved before release |
| Requirements traceability matrix | Per §16.4 |
| Release approval | Electronically signed by Quality Lead, Validation Lead, Information Security Lead, Regulatory Affairs Lead, executive authority |
| Training record | Engineering, QA, validation, operations trained on Module 2 expected state |
| Periodic review | Annual review per EU GMP Annex 11 §11; trigger reviews on every significant change |
| Data migration evidence | Backfill of the role catalogue, the default tenant matrix, and the taxonomy registries |

### 17.1 Supplier and service-provider qualification pack

| Category | Required evidence |
|---|---|
| Cloud hosting provider | Inherited from URS-01 §17.1 |
| Notification provider (e-mail) | Deliverability service-level evidence; encryption-in-transit attestation; bounce / complaint handling; sub-processor list |
| Backup and restore provider | Backup integrity evidence; restore-drill evidence with audit-chain integrity verification (URS-35); retention of backup-set audit evidence |
| KMS provider | Inherited from URS-01 §17.1 |
| Security-operations / SIEM | Alert-routing evidence for `RBAC_DENIED` clusters and Mode A / self-modification rejections |
| Right-to-audit / inspection support | Contractual right-to-audit clause; supplier commitment to support regulator inspections |

### 17.2 Inspection-ready evidence index

| Evidence item | Owner | Location / system of record | Retention | Linked requirement | Inspection use |
|---|---|---|---|---|---|
| Permission-matrix history per tenant | QA | `permission_matrix` + audit log | retain all versions | URS-02-DATA-002, URS-02-AUD-001 | reconstruct any past tenant access posture |
| Role-catalogue version history | QA | `role_catalogue` + audit log | retain history | URS-02-DATA-004 | demonstrate baseline catalogue at any inspection date |
| Taxonomy-registry version history | QA | `permission_resources`, `permission_actions` + audit log | retain history | URS-02-DATA-004 | demonstrate taxonomy baseline |
| Governance-event log | QA | `audit_log` (subset) | seven years | URS-02-AUD-001 | reconstruct who did what when in access governance |
| Periodic access review attestations | QA | governance export PDF + electronic-signature row | twenty-five years | URS-02-SEC-002 | demonstrate Annex 11 §11 compliance |
| Self-modification / Mode A rejection log | Information Security | audit log | seven years | URS-02-AUD-005 | demonstrate forensic integrity of administrative paths |
| Bulk-operation register | QA | `bulk_operations` + audit log | seven years | URS-02-BE-016 | demonstrate correlated batch governance |
| Validation evidence pack (IQ / OQ / PQ) | Validation | testing system of record | retain per release | URS-02-VAL-001..007 | release approval |
| Release approval (electronically signed) | Founder, QA, RA, Validation, Information Security | document control (URS-12) | retain per release | URS-02-VAL-007 | demonstrate authority chain for release |

---

## 18. Closed Decision and Dependency Register

### 18.1 Closed Launch Decisions Register

Every closed launch decision below is locked within this document. The table below lists each resolution and points to the section of the spec that carries the binding answer. No internal item remains open or blocking.

| ID | Item | Resolution | Spec reference |
|---|---|---|---|
| DEC-02-01 | Launch list of `permission_actions` | fixed launch action taxonomy declared as canonical | §6.1.2 |
| DEC-02-02 | Launch list of `permission_resources` | fixed launch resource taxonomy declared as canonical (one entry per Verixa module plus administrative areas) | §6.1.3 |
| DEC-02-03 | Mode A admin invariant as launch policy | Mode A is the launch policy and is binding; admin-row matrix mutation is rejected with `ADMIN_IMMUTABLE` | §0.4 standard 13, §6.5 BR-02-02, §15.2 URS-02-BE-002 |
| DEC-02-04 | Self-modification block scope | covers role change, deactivate, reactivate, remove, force password reset, matrix mutation targeting own membership, and direct provisioning of self; excludes own profile metadata, MFA enrolment, and password change by the user themselves (owned by URS-01) | §12.10, §6.5 BR-02-03 |
| DEC-02-05 | Removal-fallback trigger graph | graph enumerated; covers `created_by`, `updated_by`, `actor_user_id`, `signed_by`, `target_user_id`, `assigned_to`, `owner_id`, `investigator_id`, `classified_by`, `closed_by`, `released_by`, `dispatched_by`, `reviewed_by` references in URS-12 / 13 / 14 / 15 / 16 / 17 / 18 / 19 / 20 / 21 / 22 / 23 / 24 / 25 / 26 / 27 / 28 / 29 / 31 / 33 / 34 plus URS-04 electronic signatures | §6.1.5, §6.5 BR-02-10 |
| DEC-02-06 | Platform-only direct provisioning policy and source-tool identifiers | source-tool registry declared with six launch entries; adding a source tool is a Class 1 change requiring founder electronic signature | §6.1.4, §6.5 BR-02-11 |
| DEC-02-07 | Bulk-operation cap | two hundred users per submission; submissions exceeding the cap receive `BULK_LIMIT_EXCEEDED` and the UI auto-splits | §6.4 |
| DEC-02-08 | Shared-constant field naming (`description` vs `intendedUse`) | field name is `description` for launch; alignment with the spec preference `intendedUse` is a Class 4 cosmetic change scheduled for a subsequent release; downstream consumers MUST read `description` | §2.3 DEC-02-12 |
| DEC-02-09 | Matrix-edit digest e-mails | hourly digest to the tenant administrator queue when matrix mutation occurred in the previous hour; opt-out per tenant via security policy | §6.4, §10 |
| DEC-02-10 | SOC alert routing | alert thresholds and channels declared | §12.9 |
| DEC-02-11 | India regulatory framework applicability | tenant onboarding captures the operating jurisdictions and the RA disposition; URS-08 maintains the disposition; URS-02 inherits the resulting controls | §14 RG-02-014 |
| DEC-02-12 | Matrix-version resolver cache time-to-live | thirty-second time-to-live with event-bus invalidation on `MATRIX_VERSION_INCREMENTED` / `CLAIMS_VERSION_INCREMENTED` | §6.4 |

### 18.2 Dependencies

| ID | Dependency | Source | Impact | Blocking? | Mitigation |
|---|---|---|---|---|---|
| DEP-02-01 | URS-01 authentication, sessions, MFA, claims-version increment, force-password-reset hook | URS-01 | Substrate | Blocking | none |
| DEP-02-02 | URS-04 Controlled Approval Modal and electronic-signatures table | URS-04 | E-sign substrate | Blocking | none |
| DEP-02-03 | URS-05 Authority Profile catalogue, SoD rules | URS-05 | Authority semantics | Blocking | none |
| DEP-02-04 | URS-06 universal audit substrate and retention | URS-06 | Audit ingest | Blocking | none |
| DEP-02-05 | URS-08 tenant active gate and tenant creation seed for default matrix | URS-08 | Lifecycle | Blocking | none |
| DEP-02-06 | URS-30 notification delivery service | URS-30 | Notifications | Non-blocking | direct e-mail fallback |
| DEP-02-07 | URS-35 backup / restore / hash-chain restore drill | URS-35 | Disaster recovery | Blocking for PQ | DR drill |

---

## 19. Completeness Checklist

| Item | Yes / No | Evidence |
|---|---|---|
| Controlled-document metadata complete? | Yes | front matter |
| Approval block complete? | Yes (signatures pending) | Document Approval section |
| Version history complete? | Yes | Version History |
| Glossary complete? | Yes | §0.6 |
| Scope complete? | Yes | §2 |
| Roles and permissions complete? | Yes | §3 |
| User journeys complete? | Yes | §4 (25 journeys) |
| Front-end complete? | Yes | §5 |
| Backend complete? | Yes | §6 |
| Data model complete? | Yes | §6.2 |
| APIs complete? | Yes | §6.3 |
| Workflow complete? | Yes | §6.4 |
| Business rules complete? | Yes | §6.5 |
| Audit trail complete? | Yes | §6.6, BR-02-13, URS-02-AUD-001..005 |
| Electronic signature complete? | Yes | §6.7, URS-02-ESIG-001..004 |
| AI / Human-in-the-Loop complete or justified not applicable? | Yes (no AI; documented exclusion) | §8 |
| Reports complete? | Yes | §9 |
| Notifications complete? | Yes | §10 |
| Cross-module wiring complete? | Yes | §7 |
| Change-impact matrix complete? | Yes | §7.2 |
| Negative paths complete? | Yes | §11 |
| Security / privacy / tenant isolation complete? | Yes | §12 |
| ALCOA+ complete? | Yes | §13 |
| Regulatory mapping complete? | Yes | §14 |
| Predicate-rule applicability matrix complete or justified not applicable? | Yes | §14.1 |
| Requirements register complete? | Yes | §15 |
| Acceptance tests complete? | Yes | §16 |
| Requirements-to-test traceability complete? | Yes | §16.4 |
| Validation evidence complete? | Yes | §17, §17.1, §17.2, URS-02-VAL-001..007 |
| Supplier qualification complete or justified not applicable? | Yes | §17.1 |
| Closed launch decisions and dependencies complete? | Yes | §18 |

---

## 20. Final Module Output Quality Gate

This module URS is **final and ready for approval**. All internal closed launch decisions have been resolved within the document (§18.1). Cross-module dependencies (§18.2) are owned by named companion modules and are managed at the program level, not by Module 2.

- **Specification ready for engineering review?** Yes — every MUST requirement is declarative, atomic, and testable; front end, back end, data, API, workflow, audit, electronic signature, security, and integration are covered.
- **Specification ready for quality-validation review?** Yes — IQ / OQ / PQ scope is specified; the traceability matrix template is in §16.4.
- **Specification ready for compliance review?** Yes — ALCOA+ table, regulatory mapping, and predicate-rule applicability matrix are populated; periodic access review and audit-trail review obligations are defined.
- **Specification ready for inspector / client review?** Yes — every regulated assertion traces to a regulatory clause and to a test case; no internal item remains open.
- **Specification ready for Founder approval?** Yes.
- **Blocking gaps?** **None internal.** The cross-module dependencies in §18.2 (URS-01 authentication, URS-04 Controlled Approval Modal, URS-05 Authority Profile catalogue, URS-06 audit substrate, URS-08 active-tenant gate, URS-30 notification delivery, URS-35 backup / restore) are owned by their respective module URS documents and are tracked at the program level; their availability conditions end-to-end exercise but does not block approval of Module 2 as a controlled specification.
- **Two-step release path:**
  1. **Approved Controlled URS — released for engineering implementation and validation planning.** Reached upon signature capture in the Document Approval block; on signature capture, update the Status field on the cover page accordingly.
  2. **Released for validation execution.** Reached after URS-02-VAL evidence and the §17 validation evidence pack are satisfied.

---

## Appendix A — Bootstrap of Effective Permissions on Login

```mermaid
flowchart TD
  A([Browser opens app]) --> B[App.tsx mount]
  B --> C[useAuthInit → /auth/me (URS-01)]
  C --> D{200 OK?}
  D -- Yes --> E[Auth store: user + csrfToken + authzContext]
  E --> F[useEffectivePermissions → /access/me/permissions]
  F --> G{200 OK?}
  G -- Yes --> H[Permission store: baseRole + effectivePermissions + tenantId + matrixVersion + claimsVersion]
  H --> I[App renders authenticated routes]
  I --> J{Each request: matrixVersion stale?}
  J -- Yes --> K[Refresh permissions → re-render]
  J -- No --> I
  G -- No --> L[Render permission outage banner; disable PermissionGuard buttons]
  D -- 401 --> M[Clear store; redirect /auth/login]
  D -- 503 AUTHZ_CONTEXT_RESOLUTION_FAILED --> N[Persistent banner; AuthorityGuard disabled per URS-01]
  N --> I
```

— End of Module 2 User Requirements Specification —
