# Research — Settings Foundation (Workspace + Optional Tenant Override) (097)

## Decisions

### Decision 1 — Canonical capability registry + role mapping
- **Chosen**: Add new workspace capabilities as constants in `App\Support\Auth\Capabilities`, and map them in `App\Services\Auth\WorkspaceRoleCapabilityMap`.
  - `Capabilities::WORKSPACE_SETTINGS_VIEW`
  - `Capabilities::WORKSPACE_SETTINGS_MANAGE`
- **Rationale**: The repo already enforces “no raw strings” and uses `Capabilities::isKnown()` checks in `WorkspaceCapabilityResolver`.
- **Alternatives considered**:
  - Using raw strings (rejected: violates RBAC-UX-006).

### Decision 2 — Workspace RBAC-UX enforcement for Filament actions
- **Chosen**: Use `App\Support\Rbac\WorkspaceUiEnforcement` for Filament page actions.
- **Rationale**: This helper already implements the exact semantics required by the constitution:
  - non-member → `abort(404)` (deny-as-not-found)
  - member missing capability → `abort(403)`
  - defense-in-depth server-side guard via `before()`.
- **Alternatives considered**:
  - Ad-hoc `abort()` checks in each action (rejected: inconsistent and easier to regress).

### Decision 3 — Audit logging sink + stable action IDs
- **Chosen**:
  - Workspace-scoped audit entries: `App\Services\Audit\WorkspaceAuditLogger` (writes `audit_logs` with `workspace_id`, `tenant_id = null`).
  - Stable action identifiers: extend `App\Support\Audit\AuditActionId` enum with two new cases:
    - `WorkspaceSettingUpdated = 'workspace_setting.updated'`
    - `WorkspaceSettingReset = 'workspace_setting.reset'`
- **Rationale**: The repo already has a stable-action-id convention and tests around audit redaction and scoping; using the existing audit logger preserves sanitizer behavior (no secrets).
- **Alternatives considered**:
  - Introducing a new audit sink (rejected: violates “use existing audit sinks” precedent and increases inconsistency risk).
  - Using un-enumed string action IDs (possible, but rejected in favor of stronger standardization).

### Decision 4 — Storage model for workspace defaults + tenant overrides
- **Chosen**: Use two tables (scope-pure) rather than a single polymorphic table:
  - `workspace_settings` (workspace-owned; includes `workspace_id`; no `tenant_id`)
  - `tenant_settings` (tenant-owned; includes `workspace_id` and `tenant_id` NOT NULL)
- **Rationale**: Aligns with the constitution’s scope/ownership rule:
  - “Workspace-owned tables MUST include workspace_id and MUST NOT include tenant_id.”
  - “Tenant-owned tables MUST include workspace_id and tenant_id as NOT NULL.”
- **Alternatives considered**:
  - Single `workspace_settings` table with nullable `tenant_id` (rejected: risks violating the constitution and blurs ownership).

### Decision 5 — Resolver precedence + caching
- **Chosen**:
  - Precedence: tenant override → workspace override → system default.
  - Caching: request-local only, implemented in-memory inside the resolver (no cross-request cache store in v1).
- **Rationale**: Matches the clarified spec requirements and mirrors existing request-local caching patterns (e.g., capability resolver).
- **Alternatives considered**:
  - Cross-request cache with TTL (rejected for v1: adds invalidation complexity and isn’t required).

## Notes on existing repo patterns (evidence)

- Canonical capability registry exists: `App\Support\Auth\Capabilities`.
- Workspace capability checks and request-local caching exist: `App\Services\Auth\WorkspaceCapabilityResolver`.
- Workspace RBAC-UX enforcement helper exists: `App\Support\Rbac\WorkspaceUiEnforcement`.
- Workspace audit logger exists (with sanitizer): `App\Services\Audit\WorkspaceAuditLogger`.
