---
description: RBAC + scope-based permissions. Every route and action gated.
globs: ["app/Livewire/**", "routes/**", "resources/views/**"]
---

# Permissions — Gate Everything

## Permission Format

`{module}.{action}` — e.g., `participants.list`, `invoices.create`, `attendance.mark`

## Enforcement Points (ALL required)

1. **Route middleware**: `->middleware('permission:module.action')`
2. **Livewire mount**: `$this->authorize('module.action')`
3. **Service layer**: scope-check for specific records
4. **Blade**: `@can('module.action')` wraps buttons/links
5. **Query scope**: list queries filtered by user's scope

## Scope Resolution

When checking if user can access a SPECIFIC record:
```
'academy' → true (tenant scope already applied)
'branch' → record.branch_id === user.branch_id
'own' → record.created_by === user.id
'own_groups' → record's group is in user's assigned groups
'own_children' → record's participant is in guardian's children
```

## UI Rules

- No permission = element NOT rendered (not greyed out)
- Navigation items: only show what user can access
- Action buttons (create/edit/delete): gated individually
- Export buttons: require `{module}.export`
- Financial columns: hidden from non-financial roles

## Role Hierarchy

Level determines who can assign whom:
```
super_admin(100) > academy_owner(90) > academy_admin(80) > branch_manager(70)
> head_trainer(60) > trainer(50) > receptionist(40) > accountant(35)
> data_entry(30) > parent(5)
```

User can ONLY assign roles with level LOWER than their own.
