TenantAtlas/specs/097-settings-foundation/plan.md
ahmido e241e27853 Settings foundation: workspace controls (#119)
Implements the Settings foundation workspace controls.

Includes:
- Settings foundation UI/controls scoped to workspace context
- Related onboarding/consent flow adjustments as included in branch history

Testing:
- `vendor/bin/sail artisan test --compact --no-ansi --filter=SettingsFoundation`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #119
2026-02-16 01:11:24 +00:00

8.1 KiB
Raw Permalink Blame History

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)

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