# Tasks: Settings Foundation (Workspace + Optional Tenant Override) (097) **Input**: Design documents from `specs/097-settings-foundation/` (spec.md, plan.md, research.md, data-model.md, contracts/) **Prerequisites**: specs/097-settings-foundation/plan.md (required), specs/097-settings-foundation/spec.md (required for user stories) **Tests**: REQUIRED (Pest) for all runtime behavior changes in this repo. **RBAC**: Enforce 404 vs 403 semantics via canonical helpers + capability registry (no raw strings). **Audit**: DB-only mutations intentionally skip OperationRun; every successful update/reset MUST write a workspace-scoped audit entry. ## Phase 1: Setup (Shared Infrastructure) - [X] T001 Re-run SpecKit prerequisites and confirm FEATURE_DIR via .specify/scripts/bash/check-prerequisites.sh - [X] T002 Review RBAC-UX enforcement patterns in app/Support/Rbac/WorkspaceUiEnforcement.php - [X] T003 Review existing capability registry + caching patterns in app/Support/Auth/Capabilities.php and app/Services/Auth/WorkspaceCapabilityResolver.php - [X] T004 Review workspace audit patterns + stable action IDs in app/Services/Audit/WorkspaceAuditLogger.php and app/Support/Audit/AuditActionId.php --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Shared primitives (capabilities, persistence, policies) needed by all stories. - [X] T005 Add settings capabilities in app/Support/Auth/Capabilities.php (WORKSPACE_SETTINGS_VIEW, WORKSPACE_SETTINGS_MANAGE) - [X] T006 Map new capabilities to roles in app/Services/Auth/WorkspaceRoleCapabilityMap.php (Owner/Manager manage; Operator/Readonly view) - [X] T007 Create workspace_settings table migration in database/migrations/*_create_workspace_settings_table.php (workspace-owned: MUST NOT include tenant_id) - [X] T008 [P] Create tenant_settings table migration in database/migrations/*_create_tenant_settings_table.php (tenant-owned: MUST include tenant_id NOT NULL) - [X] T009 [P] Add Eloquent models in app/Models/WorkspaceSetting.php and app/Models/TenantSetting.php - [X] T010 [P] Add model factories in database/factories/WorkspaceSettingFactory.php and database/factories/TenantSettingFactory.php - [X] T011 Add policy for settings writes in app/Policies/WorkspaceSettingPolicy.php (view/manage using WorkspaceCapabilityResolver) - [X] T012 Register policy mapping in app/Providers/AuthServiceProvider.php **Checkpoint**: Capabilities + persistence + policies exist; user story work can begin. --- ## Phase 3: User Story 1 — Manage workspace settings safely (Priority: P1) 🎯 MVP **Goal**: Workspace managers can update/reset the pilot setting with validation + audit logging. **Independent Test**: A manager changes backup.retention_keep_last_default, sees it reflected, and the retention fallback uses it when schedule retention is null. ### Tests (write first) - [X] T013 [P] [US1] Add manage workflow tests in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php - [X] T014 [P] [US1] Add audit logging tests in tests/Feature/SettingsFoundation/WorkspaceSettingsAuditTest.php - [X] T015 [P] [US1] Add resolver caching tests in tests/Unit/SettingsFoundation/SettingsResolverCacheTest.php - [X] T016 [P] [US1] Add pilot integration test for retention fallback in tests/Feature/SettingsFoundation/RetentionFallbackUsesWorkspaceDefaultTest.php - [X] T038 [P] [US1] Add per-schedule override precedence test (schedule override wins) in tests/Feature/SettingsFoundation/RetentionScheduleOverrideWinsTest.php - [X] T039 [P] [US1] Add validation negative-path test: unknown setting key is rejected; no changes persisted; no audit entry created - [X] T040 [P] [US1] Add validation negative-path test: invalid value (wrong type/out-of-range) is rejected; no changes persisted; no audit entry created ### Implementation - [X] T017 [P] [US1] Create setting definition DTO in app/Support/Settings/SettingDefinition.php - [X] T018 [P] [US1] Create registry for known settings in app/Support/Settings/SettingsRegistry.php (include backup.retention_keep_last_default default=30 + validation) - [X] T019 [P] [US1] Implement resolver with precedence + request-local cache in app/Services/Settings/SettingsResolver.php - [X] T020 [P] [US1] Add stable audit action IDs in app/Support/Audit/AuditActionId.php (workspace_setting.updated, workspace_setting.reset) - [X] T021 [US1] Implement writer (validate, persist, reset deletes row, audit before/after) in app/Services/Settings/SettingsWriter.php - [X] T022 [US1] Add Filament workspace Settings page shell in app/Filament/Pages/Settings/WorkspaceSettings.php (uses WorkspaceUiEnforcement) - [X] T023 [US1] Register the Settings page in the admin panel navigation in app/Providers/Filament/AdminPanelProvider.php - [X] T024 [US1] Implement Save action via Action::make(...)->action(...) in app/Filament/Pages/Settings/WorkspaceSettings.php - [X] T025 [US1] Implement Reset action with ->requiresConfirmation() and ->action(...) in app/Filament/Pages/Settings/WorkspaceSettings.php - [X] T026 [US1] Wire retention fallback to resolver when schedule retention is null in app/Jobs/ApplyBackupScheduleRetentionJob.php --- ## Phase 4: User Story 2 — View settings without the ability to change them (Priority: P2) **Goal**: Workspace operators/readonly can view settings but cannot save/reset; server-side 403 on mutation. **Independent Test**: A view-only member opens the Settings page and can see values, but attempts to save/reset return 403 and no audit entry is created. ### Tests (write first) - [X] T027 [P] [US2] Add view-only access tests (view OK, mutation forbidden) in tests/Feature/SettingsFoundation/WorkspaceSettingsViewOnlyTest.php - [X] T037 [P] [US2] Add non-member deny-as-not-found tests (404) in tests/Feature/SettingsFoundation/WorkspaceSettingsNonMemberNotFoundTest.php ### Implementation - [X] T028 [US2] Gate page access by view capability and gate mutations by manage capability in app/Filament/Pages/Settings/WorkspaceSettings.php - [X] T029 [US2] Ensure Save/Reset actions cannot execute for view-only users (server-side enforcement) in app/Filament/Pages/Settings/WorkspaceSettings.php --- ## Phase 5: User Story 3 — Tenant overrides take precedence (backend-ready) (Priority: P3) **Goal**: Resolver supports tenant overrides; tenant override wins over workspace override; rejects tenant/workspace mismatch. **Independent Test**: With tenant override present, resolving returns tenant value; without it, returns workspace; without either, returns system default. ### Tests (write first) - [X] T030 [P] [US3] Add tenant precedence tests (default/workspace/tenant) in tests/Unit/SettingsFoundation/SettingsResolverTenantPrecedenceTest.php - [X] T031 [P] [US3] Add tenant/workspace mismatch rejection test in tests/Feature/SettingsFoundation/TenantOverrideScopeSafetyTest.php ### Implementation - [X] T032 [US3] Extend resolver to read tenant overrides from tenant_settings (same workspace) in app/Services/Settings/SettingsResolver.php - [X] T033 [US3] Add tenant override write/reset methods (backend-ready) in app/Services/Settings/SettingsWriter.php --- ## Phase 6: Polish & Cross-Cutting Concerns - [X] T034 [P] Run formatting for changed files via vendor/bin/sail bin pint --dirty (see specs/097-settings-foundation/quickstart.md) - [X] T035 Run focused tests for this feature via vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation (see specs/097-settings-foundation/quickstart.md) - [X] T036 Run the full suite via vendor/bin/sail artisan test --compact (see specs/097-settings-foundation/quickstart.md) --- ## Dependencies & Execution Order ### User Story Dependency Graph - Setup → Foundational - Foundational → US1 - US1 → US2 - US1 → US3 ### Parallel Opportunities (examples) - US1: T013–T016 (tests) and T017–T020 (core classes + enum) can be executed in parallel. - Foundational: T007–T012 can be split across DB/model/policy work in parallel. Example: In US1, execute T013, T014, T017, and T018 concurrently (different files; no dependencies). ## MVP Scope Suggestion - MVP = Phases 1–3 (through US1) to ship a workspace settings foundation with the pilot setting wired into retention fallback.