---
description: Enforces multi-tenancy rules. Every query scoped to academy.
globs: ["app/**/*.php", "database/migrations/**"]
---

# Multi-Tenancy — Every Query Scoped

## BelongsToAcademy Trait

Every domain model (except Organization itself) MUST use the `BelongsToAcademy` trait. This trait:
- Auto-sets `academy_id` on creating
- Adds global scope filtering by `app('current_academy')`

## Tables WITHOUT academy_id (exhaustive list)

- `organizations` (IS the tenant)
- `migrations`, `jobs`, `failed_jobs`, `cache`, `sessions`, `password_reset_tokens`
- `permissions` (shared definitions)

Everything else HAS `academy_id`. No exceptions.

## Cross-Tenant Safety

- Unique constraints MUST include `academy_id`: `unique(['academy_id', 'slug'])`
- FK references MUST be within same tenant (a payment can't reference another academy's invoice)
- Seeded data (accounts, roles) is PER academy — not shared
- SuperAdmin bypasses scope via `withoutGlobalScope('academy')`
- Regular users NEVER bypass scope

## When Creating Models

```php
use App\Domain\Shared\Traits\BelongsToAcademy;
use App\Domain\Shared\Traits\HasUuid;

class MyModel extends Model
{
    use BelongsToAcademy, HasUuid, SoftDeletes;
}
```
