# Phase 9 — Full Authorization Audit and Enforcement

## Goal

Phase 9 closes the system-wide roles and permissions rollout by making authorization coverage auditable across HTTP and non-HTTP execution surfaces, eliminating ordinary direct role-name authorization, and removing the final unused permission compatibility wrapper.

Earlier phases established the canonical Spatie permission catalog, Laravel Gate/policies, branch isolation, maker-checker controls, and granular permissions across business modules. This final enforcement step does not expand privileges by default. It verifies that the resulting authorization architecture has no silent coverage gaps and prevents future regressions.

## Coverage model

The `authorization:coverage-audit` command complements, rather than replaces, the Phase 0 `authorization:inventory` command and the Phase 9 `authorization:audit-role-checks` command.

### HTTP routes

Every application-managed route must be one of:

- permission-protected with `permission:*` middleware;
- policy-protected with `can:*` middleware;
- explicitly classified in the existing self-service, authenticated-support, or public authorization registries.

A route that falls outside these classifications is a strict audit failure.

### Direct role checks

Ordinary runtime authorization must not depend on direct role-name checks such as `hasRole(...)`, `hasAnyRole(...)`, role middleware, or Blade role directives.

The only approved role-based authorization exception is the protected `super-administrator` global `Gate::before` bypass in `AppServiceProvider`. That exception is declared explicitly in `config/authorization_role_check_audit.php` with its reason, so it remains visible to the audit instead of becoming an undocumented authorization path.

`authorization:audit-role-checks --strict` is enforced in CI. Any new unapproved direct role check fails the build. Outside the documented super-administrator bypass, roles remain permission bundles and administration metadata; permissions and policies remain the decision boundary.

### Policies

Every PHP policy under `app/Policies` must be explicitly registered through `Gate::policy(...)` in `AppServiceProvider`.

This prevents a policy class from existing in source while never participating in runtime authorization.

### Artisan commands and scheduled mutations

Every application command under `app/Console/Commands` is inventoried by its command signature.

Commands scheduled from `routes/console.php` are classified as system-scheduler execution. The audit fails if a scheduled command does not resolve to an application command signature.

Other application commands are classified as operator-console surfaces. They are intentionally not converted into web permissions: access to the production console/deployment environment is an operational boundary, while any user-facing HTTP capability remains permission/policy protected.

### Queue jobs

Every job under `app/Jobs` is inventoried as an internal queued execution surface and must expose an auditable `handle` method.

Jobs do not receive independent end-user permissions. Authorization belongs at the dispatch boundary; queued work executes the already-authorized workflow asynchronously.

### Blade/UI authorization

The audit records Blade `@can`, `@cannot`, and `@canany` call sites and state-changing form markers as UI coverage evidence.

Blade visibility is defense-in-depth only. Server-side route, policy, service, branch-scope, and maker-checker rules remain the security boundary.

## Compatibility cleanup

The temporary `User::hasPermission()` wrapper is removed in this step because runtime application code has been migrated to Laravel's canonical `can()`, `cannot()`, Gate, policy, and permission middleware APIs.

The Spatie-to-legacy assignment mirror is **not** dropped in this commit. `role_user` and `permission_role` remain rollback/production-validation mirrors until a separate destructive migration is explicitly approved after production parity has been observed.

## Existing controls preserved

Phase 9 preserves:

- the canonical permission catalog and default role matrix;
- the protected super-administrator `Gate::before` convention as the single documented direct-role exception;
- branch access as a separate data-scope concern;
- service/domain maker-checker rules that must also constrain super-administrators;
- the Phase 0 strict route inventory and permission-record validation;
- all existing workflow-state and financial atomicity protections;
- the temporary legacy pivot mirror for rollback confidence.

## CI gate

Foundation CI runs all of these strict authorization gates:

```bash
php artisan authorization:inventory --json --strict
php artisan authorization:coverage-audit --json --strict
php artisan authorization:audit-role-checks --json --strict
php artisan authorization:catalog-check --json
php artisan authorization:spatie-bridge --json
```

A future change that adds an uncovered application route, an unregistered policy, an unresolved scheduled command, a malformed queue job, or an unapproved direct role-based authorization check will fail CI before merge.

## Permission catalog

This step adds no new permission solely for auditing. The catalog remains the Phase 8 catalog unless a separately justified business capability requires a new permission in a future change.

## Verification

`tests/Feature/Phase9FullAuthorizationAuditTest.php` verifies:

1. the full coverage audit passes in strict mode;
2. the direct role-check audit passes in strict mode with no unapproved role checks;
3. every policy file is registered;
4. every scheduled command resolves to an application command;
5. queue/command execution surfaces contain no audit errors;
6. the original Phase 0 strict HTTP inventory continues to pass;
7. the temporary `User::hasPermission()` compatibility method no longer exists.

## Remaining post-RBAC cleanup

Dropping `role_user`, `permission_role`, the bridge synchronizer, and bridge-only migration/recovery paths is intentionally deferred to a separate destructive cleanup after production parity validation. That operation must have its own rollback plan and database backup verification.
