TenantAtlas/specs/098-settings-slices-v1-backup-drift-ops/tasks.md
ahmido c57f680f39 feat: Workspace settings slices v1 (backup, drift, operations) (#120)
Implements Spec 098: workspace-level settings slices for Backup retention, Drift severity mapping, and Operations retention/threshold.

Spec
- specs/098-settings-slices-v1-backup-drift-ops/spec.md

What changed
- Workspace Settings page: grouped Backup/Drift/Operations sections, unset-input UX w/ helper text, per-setting reset actions (confirmed)
- Settings registry: adds/updates validation + normalization (incl. drift severity mapping normalization to lowercase)
- Backup retention: adds workspace default + floor clamp; job clamps effective keep-last up to floor
- Drift findings: optional workspace severity mapping; adds `critical` severity support + badge mapping
- Operations pruning: retention computed per workspace via settings; scheduler unchanged; stuck threshold is storage-only

Safety / Compliance notes
- Filament v5 / Livewire v4: no Livewire v3 usage; relies on existing Filament v5 + Livewire v4 stack
- Provider registration unchanged (Laravel 11+/12 uses bootstrap/providers.php)
- Destructive actions: per-setting reset uses Filament actions with confirmation
- Global search: not affected (no resource changes)
- Assets: no new assets registered; no `filament:assets` changes

Tests
- vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php \
  tests/Feature/SettingsFoundation/WorkspaceSettingsViewOnlyTest.php \
  tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php \
  tests/Feature/Drift/DriftPolicySnapshotDriftDetectionTest.php \
  tests/Feature/Scheduling/PruneOldOperationRunsScheduleTest.php \
  tests/Unit/Badges/FindingBadgesTest.php

Formatting
- vendor/bin/sail bin pint --dirty

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #120
2026-02-16 03:18:33 +00:00

8.4 KiB

description
Task list for 098-settings-slices-v1-backup-drift-ops

Tasks: 098 — Settings Slices v1 (Backup + Drift + Operations)

Input: Design documents from /specs/098-settings-slices-v1-backup-drift-ops/ (spec.md, plan.md) Tests: REQUIRED (Pest) — runtime behavior changes. Scope: workspace-level settings; DB-only rendering; no Graph calls.

Phase 1: Setup (Shared Infrastructure)

  • T001 Confirm feature branch + clean working tree in specs/098-settings-slices-v1-backup-drift-ops/spec.md
  • T002 Verify Settings Foundation dependency is present by locating SettingsRegistry in app/Support/Settings/SettingsRegistry.php
  • T003 [P] Capture baseline behaviors + constants and record them in specs/098-settings-slices-v1-backup-drift-ops/plan.md by reviewing app/Jobs/ApplyBackupScheduleRetentionJob.php, app/Services/Drift/DriftFindingGenerator.php, app/Jobs/PruneOldOperationRunsJob.php, routes/console.php

Phase 2: Foundational (Blocking Prerequisites)

Purpose: Shared settings primitives + Workspace Settings page patterns used by all slices.

  • T004 Update Settings registry validation to match spec (max 365) in app/Support/Settings/SettingsRegistry.php
  • T005 [P] Add per-setting reset UX pattern scaffolding (no global reset) in app/Filament/Pages/Settings/WorkspaceSettings.php
  • T006 Add “unset input + helper text shows default/effective” support in app/Filament/Pages/Settings/WorkspaceSettings.php
  • T007 [P] Update existing workspace settings RBAC tests for new per-setting reset actions in tests/Feature/SettingsFoundation/WorkspaceSettingsViewOnlyTest.php
  • T008 [P] Update existing manage test to assert per-setting reset (not header reset) in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php

Checkpoint: Workspace Settings page supports per-setting resets and unset presentation without changing behavior.


Phase 3: User Story 1 — Backup retention defaults (Priority: P1) 🎯 MVP

Goal: Workspace overrides for backup retention default + min floor; job clamps effective keep-last to floor. Independent Test: Run ApplyBackupScheduleRetentionJob behavior with/without workspace overrides and verify clamping.

Tests (US1)

  • T009 [P] [US1] Add/extend retention job tests to cover default + floor clamp in tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php
  • T010 [P] [US1] Add validation tests for backup settings bounds (1..365) via SettingsWriter in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php

Implementation (US1)

  • T011 [US1] Register backup floor setting and tighten keep-last rules (1..365) in app/Support/Settings/SettingsRegistry.php
  • T012 [US1] Extend Workspace Settings UI: Backup section adds backup.retention_keep_last_default + backup.retention_min_floor fields with per-setting reset actions in app/Filament/Pages/Settings/WorkspaceSettings.php
  • T013 [US1] Update Workspace Settings save logic: empty field triggers reset (delete override) instead of persisting null in app/Filament/Pages/Settings/WorkspaceSettings.php
  • T014 [US1] Apply floor clamping for both schedule override and resolved default in app/Jobs/ApplyBackupScheduleRetentionJob.php

Phase 4: User Story 2 — Drift severity mapping (Priority: P2)

Goal: Workspace-level finding_type → severity mapping with default medium and strict validation; normalize severities to lowercase. Independent Test: Generate drift findings and assert severity uses mapping when present; saving invalid mapping is rejected.

Tests (US2)

  • T015 [P] [US2] Add drift generator test asserting default severity remains medium when no mapping set in tests/Feature/Drift/DriftPolicySnapshotDriftDetectionTest.php
  • T016 [P] [US2] Add drift generator test asserting mapped severity is applied when mapping exists in tests/Feature/Drift/DriftPolicySnapshotDriftDetectionTest.php
  • T017 [P] [US2] Add settings save validation tests for drift severity mapping JSON shape + allowed values in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php

Implementation (US2)

  • T018 [US2] Add Finding::SEVERITY_CRITICAL constant and ensure severity domain remains stable in app/Models/Finding.php
  • T019 [US2] Extend finding severity badge mapping to include critical (BADGE-001 compliant) in app/Support/Badges/Domains/FindingSeverityBadge.php
  • T020 [US2] Register drift.severity_mapping setting with JSON validation + canonical normalization (lowercase values, string keys) in app/Support/Settings/SettingsRegistry.php
  • T021 [US2] Update DriftFindingGenerator to resolve workspace severity mapping (via SettingsResolver in workspace context) and apply mapped severity (fallback medium) in app/Services/Drift/DriftFindingGenerator.php
  • T022 [US2] Extend Workspace Settings UI: Drift section adds JSON textarea for drift.severity_mapping with unset behavior + per-setting reset in app/Filament/Pages/Settings/WorkspaceSettings.php

Phase 5: User Story 3 — Operations retention + stuck threshold (Priority: P3)

Goal: Workspace-level operations.operation_run_retention_days drives pruning; operations.stuck_run_threshold_minutes is stored only. Independent Test: Create old/new OperationRuns across workspaces and verify prune respects per-workspace retention; stuck threshold persists and reloads.

Tests (US3)

  • T023 [P] [US3] Update pruning schedule test to match new job behavior (per-workspace retention) in tests/Feature/Scheduling/PruneOldOperationRunsScheduleTest.php
  • T024 [P] [US3] Add prune job test verifying per-workspace retention cutoff using workspace settings in tests/Feature/Scheduling/PruneOldOperationRunsScheduleTest.php
  • T025 [P] [US3] Add workspace settings save/reset tests for operations keys in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php

Implementation (US3)

  • T026 [US3] Register operations settings keys with correct bounds in app/Support/Settings/SettingsRegistry.php
  • T027 [US3] Refactor PruneOldOperationRunsJob to compute retention per workspace (SettingsResolver + Workspace iteration) and prune by workspace_id in app/Jobs/PruneOldOperationRunsJob.php
  • T028 [US3] Ensure scheduler continues to enqueue prune job without needing a parameter in routes/console.php
  • T029 [US3] Extend Workspace Settings UI: Operations section adds operation_run_retention_days + stuck_run_threshold_minutes fields with unset behavior + per-setting reset in app/Filament/Pages/Settings/WorkspaceSettings.php

Phase 6: Polish & Cross-Cutting Concerns

  • T030 [P] Confirm multi-key save emits one audit entry per key changed by reviewing app/Services/Settings/SettingsWriter.php and Workspace Settings save flow in app/Filament/Pages/Settings/WorkspaceSettings.php
  • T031 [P] Run Pint formatting on touched files via vendor/bin/sail bin pint --dirty (e.g., app/Filament/Pages/Settings/WorkspaceSettings.php, app/Support/Settings/SettingsRegistry.php, app/Jobs/ApplyBackupScheduleRetentionJob.php, app/Services/Drift/DriftFindingGenerator.php, app/Jobs/PruneOldOperationRunsJob.php)
  • T032 Run focused settings UI tests via vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php
  • T033 Run focused drift tests via vendor/bin/sail artisan test --compact tests/Feature/Drift/DriftPolicySnapshotDriftDetectionTest.php
  • T034 Run focused pruning tests via vendor/bin/sail artisan test --compact tests/Feature/Scheduling/PruneOldOperationRunsScheduleTest.php
  • T035 [P] Add an automated regression test asserting per-setting reset actions require confirmation (destructive-like) in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php
  • T036 [P] Add an automated regression test asserting multi-key save produces one audit entry per key changed (FR-008a) in tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php

Dependencies & Execution Order

  • Setup (Phase 1) → Foundational (Phase 2) → US1 (Phase 3) → US2 (Phase 4) → US3 (Phase 5) → Polish (Phase 6)

Parallel Execution Examples

US1 parallel example: T009 + T011 + T014
US2 parallel example: T015 + T018 + T021
US3 parallel example: T023 + T027 + T029

Implementation Strategy

  • MVP = US1 only (backup defaults + floor clamp) with updated Workspace Settings UX and tests.
  • Then US2 (drift mapping) and US3 (operations retention/threshold) as independent increments.