# Implementation Plan: Settings Foundation (Workspace + Optional Tenant Override)

**Branch**: `097-settings-foundation` | **Date**: 2026-02-15 | **Spec**: `/specs/097-settings-foundation/spec.md`
**Input**: Feature specification from `/specs/097-settings-foundation/spec.md`

**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.

## Summary

Implement a workspace-scoped settings substrate with a canonical registry + validation, a resolver that applies precedence (tenant override → workspace override → system default), a workspace Settings page in Filament, and audit logging for each successful mutation.

For v1, the pilot setting is `backup.retention_keep_last_default` (system default `30`), stored as a workspace override and later consumed by backup retention behavior when a schedule has no explicit `retention_keep_last`.

## Technical Context

**Language/Version**: PHP 8.4 (Laravel 12)  
**Primary Dependencies**: Filament v5, Livewire v4, Laravel Sail  
**Storage**: PostgreSQL (Sail local)  
**Testing**: Pest v4 (PHPUnit 12)  
**Target Platform**: Web app (Filament admin panel)
**Project Type**: Laravel monolith (Filament pages + services + jobs)  
**Performance Goals**: settings resolution is request-local cached; repeated resolves for the same (workspace, optional tenant, domain, key) do not issue repeated DB reads  
**Constraints**: strict workspace isolation (non-member 404), capability-gated mutations (member without capability 403), audit each successful mutation, no secrets in audit metadata  
**Scale/Scope**: low-volume admin configuration with high auditability and correctness requirements

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

- Inventory-first: clarify what is “last observed” vs snapshots/backups
- Read/write separation: any writes require preview + confirmation + audit + tests
- Graph contract path: Graph calls only via `GraphClientInterface` + `config/graph_contracts.php`
- Deterministic capabilities: capability derivation is testable (snapshot/golden tests)
- RBAC-UX: two planes (/admin vs /system) remain separated; cross-plane is 404; tenant-context routes (/admin/t/{tenant}/...) are tenant-scoped; canonical workspace-context routes under /admin remain tenant-safe; non-member tenant/workspace access is 404; member-but-missing-capability is 403; authorization checks use Gates/Policies + capability registries (no raw strings, no role-string checks)
- Workspace isolation: non-member workspace access is 404; tenant-plane routes require an established workspace context; workspace context switching is separate from Filament Tenancy
- RBAC-UX: destructive-like actions require `->requiresConfirmation()` and clear warning text
- RBAC-UX: global search is tenant-scoped; non-members get no hints; inaccessible results are treated as not found (404 semantics)
- Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked
- Run observability: long-running/remote/queued work creates/reuses `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log; auth handshake exception OPS-EX-AUTH-001 allows synchronous outbound HTTP on `/auth/*` without `OperationRun`
- Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter
- Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens
- Badge semantics (BADGE-001): status-like badges use `BadgeCatalog` / `BadgeRenderer`; no ad-hoc mappings; new values include tests
- Filament UI Action Surface Contract: for any new/modified Filament Resource/RelationManager/Page, define Header/Row/Bulk/Empty-State actions, ensure every List/Table has a record inspection affordance (prefer `recordUrl()` clickable rows; do not render a lone View row action), keep max 2 visible row actions with the rest in “More”, group bulk actions, require confirmations for destructive actions (typed confirmation for large/bulk where applicable), write audit logs for mutations, enforce RBAC via central helpers (non-member 404, member missing capability 403), and ensure CI blocks merges if the contract is violated or not explicitly exempted

**Result (pre-Phase 0)**: PASS.

- This feature is DB-only and completes in <2s; it intentionally does not create an `OperationRun`, but MUST emit workspace-scoped audit entries on each successful mutation.
- No Graph calls are introduced.
- RBAC enforcement must use the canonical capability registry (`App\Support\Auth\Capabilities`) and workspace UI enforcement helper (`App\Support\Rbac\WorkspaceUiEnforcement`) for Filament surfaces.

## Project Structure

### Documentation (this feature)

```text
specs/097-settings-foundation/
├── plan.md              # This file (/speckit.plan command output)
├── research.md          # Phase 0 output (/speckit.plan command)
├── data-model.md        # Phase 1 output (/speckit.plan command)
├── quickstart.md        # Phase 1 output (/speckit.plan command)
├── contracts/           # Phase 1 output (/speckit.plan command)
└── tasks.md             # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```

### Source Code (repository root)

```text
app/
├── Filament/
│   └── Pages/
│       └── Settings/
├── Models/
├── Policies/
├── Providers/
├── Services/
└── Support/
  ├── Audit/
  ├── Auth/
  ├── Rbac/
  └── Settings/

config/
database/migrations/
tests/
```

Expected additions (at implementation time):

```text
app/Support/Settings/SettingDefinition.php
app/Support/Settings/SettingsRegistry.php
app/Services/Settings/SettingsResolver.php
app/Services/Settings/SettingsWriter.php
app/Models/WorkspaceSetting.php
app/Models/TenantSetting.php
app/Filament/Pages/Settings/WorkspaceSettings.php
app/Policies/WorkspaceSettingPolicy.php
database/migrations/*_create_workspace_settings_table.php
database/migrations/*_create_tenant_settings_table.php
tests/Feature/SettingsFoundation/*
tests/Unit/SettingsFoundation/*
```

**Structure Decision**: Laravel monolith (Filament admin + Eloquent models + services + Pest tests). No new top-level directories.

## Phase 0 — Outline & Research

Deliverable: `research.md` with all implementation decisions resolved.

Key questions resolved in research:

- Which existing RBAC enforcement helper to use for workspace-scoped Filament pages.
- Which audit logger to use for workspace-scoped audit events and how to represent stable action IDs.
- How to model workspace defaults + tenant overrides while staying compliant with the constitution’s scope/ownership rules.

## Phase 1 — Design & Contracts

Deliverables: `data-model.md`, `contracts/*`, `quickstart.md`.

- Data model defines workspace settings and tenant overrides with strict workspace isolation.
- Contracts define the conceptual read/write/reset operations (even if the first implementation is driven via Filament/Livewire).
- Quickstart defines how to run migrations, format, and execute focused tests.

## Phase 2 — Planning

Implementation sequence (high-level):

1. Add capability constants in `App\Support\Auth\Capabilities` and map them in `App\Services\Auth\WorkspaceRoleCapabilityMap`.
2. Add settings registry + validation (`SettingDefinition`, `SettingsRegistry`) including the pilot setting.
3. Add storage + resolver + writer services with request-local caching.
4. Add audit action IDs (stable) and emit workspace audit entries for update/reset.
5. Add Filament workspace Settings page with Save + Reset actions, gated using `WorkspaceUiEnforcement`.
6. Add Pest tests for precedence, RBAC (404/403), validation, caching behavior, and audit entries.

**Constitution re-check (post-design)**: expected PASS (DB-only mutations audited; strict 404/403 semantics; capability registry is canonical).

## Complexity Tracking

> **Fill ONLY if Constitution Check has violations that must be justified**

| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| None | N/A | N/A |
