8.1 KiB
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/*withoutOperationRun - 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)
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)
app/
├── Filament/
│ └── Pages/
│ └── Settings/
├── Models/
├── Policies/
├── Providers/
├── Services/
└── Support/
├── Audit/
├── Auth/
├── Rbac/
└── Settings/
config/
database/migrations/
tests/
Expected additions (at implementation time):
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):
- Add capability constants in
App\Support\Auth\Capabilitiesand map them inApp\Services\Auth\WorkspaceRoleCapabilityMap. - Add settings registry + validation (
SettingDefinition,SettingsRegistry) including the pilot setting. - Add storage + resolver + writer services with request-local caching.
- Add audit action IDs (stable) and emit workspace audit entries for update/reset.
- Add Filament workspace Settings page with Save + Reset actions, gated using
WorkspaceUiEnforcement. - 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 |