# ADR 0001 — Sprint 1 Foundation Decisions

**Status**: Accepted
**Deciders**: Product owner, Solution architect
**Consulted**: Backend lead, Frontend lead, QA lead
**Superseded by**: —

## Context

Sprint 1 lays the foundation for every future module. Choices made here become
irreversible after modules land, so we lock them in this ADR.

## Decisions

### 1. Framework & language

- Laravel 12 on PHP 8.3+ (native enums, readonly, first-class callable syntax).
- MySQL 8 (JSON columns, generated columns, window functions).
- Rejected: Symfony (team velocity), Rails (deployment surface), Node/NestJS
  (SQL story weaker), PostgreSQL (customer ops team is MySQL-only today).

### 2. Frontend approach

- Blade + Bootstrap 5 + Vanilla JS (ES modules) + AJAX.
- Rejected: Livewire (extra latency + coupling), Inertia + React/Vue (bundle
  size, hiring pool for Indian SMB developers).
- ApexCharts for dashboards (MIT, tree-shakeable, no license fee).

### 3. RBAC

- `spatie/laravel-permission`, but roles subclassed via `App\Models\Role` to add
  `description` + `is_system` and audit hooks.
- Permissions are DYNAMIC: each module registers its permissions at boot via
  `UserPermissionService::registerBatch()`. No hardcoded strings in policies —
  policies check permission names, not role names.

### 4. Auth

- Laravel Breeze primitives (session-based), no Sanctum tokens in Sprint 1.
- Rate limiting via built-in RateLimiter (login: 5/15min, password-reset: 5/60min).
- Password policy driven by settings (min length, character mix, expiry days).
- Login history stored in an append-only `login_histories` table with device
  fingerprint parsed heuristically from User-Agent.

### 5. Multi-tenancy

- Single-database, shared schema, `company_id` (aliased `tenant_id` internally).
- Sprint 1 seeds exactly one Company. `active_company()` helper resolves it.
- Sprint 2 introduces `BelongsToTenant` trait + global scope. Migrations already
  add `company_id` FKs so no schema drift will be required.

### 6. Audit

- `spatie/laravel-activitylog` on User, Company, Role, Setting.
- Extra `company_id` column added to `activity_log`.
- Audit rows are never mutated. If a source row soft-deletes, its history stays.

### 7. Settings

- Key/value JSON table, tenant-scoped, grouped by `SettingGroup` enum.
- Cached per tenant; `SettingObserver` flushes on write.

### 8. Theme

- CSS custom properties as the single source of truth.
- Two persistence scopes: per-user (`users.theme`, `users.density`) and
  per-company (`companies.meta.theme.*`).
- Server injects a `<style id="theme-vars">` block on every render; JS may
  overwrite it live for previews.

### 9. Directory structure

- `app/Actions/` for invokable single-purpose mutations.
- `app/Services/` for stateful composition.
- `app/Domain/Enums/` for typed value objects.
- `resources/js/core/` for framework-agnostic building blocks.
- `resources/js/modules/` for per-feature bindings.
- `resources/sass/{abstracts,base,components,layout,pages}/`.

### 10. JSON envelope

Every AJAX response uses `{ok, message, data, redirect, errors}`. Controllers
use `$this->ok(...)` / `$this->fail(...)` helpers on the base Controller.

## Consequences

- Any deviation from the folder/naming rules requires a new ADR.
- Sprint 2+ modules must register their permissions via
  `UserPermissionService::registerBatch()` — do not hand-insert rows.
- Any new theme variable MUST be added to `_tokens.scss` AND the `themes` table
  (so DB-driven themes remain complete).
