# 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 |