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
6.4 KiB
Implementation Plan: 098 — Settings Slices v1 (Backup + Drift + Operations)
Branch: 098-settings-slices-v1-backup-drift-ops | Date: 2026-02-16 | Spec: specs/098-settings-slices-v1-backup-drift-ops/spec.md
Input: Feature specification from specs/098-settings-slices-v1-backup-drift-ops/spec.md
Summary
Extend the existing Settings Foundation to expose five additional workspace-level keys on the Workspace Settings page, with strict validation, per-setting reset-to-default (confirmed), and audit logging per key changed.
Apply those settings to three behavior paths:
- Backup retention: enforce a workspace-configurable retention default and a workspace-configurable retention floor (effective values clamped up to the floor).
- Drift severities: allow workspace-configurable
finding_type → severitymapping (defaultmedium, values normalized). - Operations: allow workspace-configurable operation run retention days for pruning and store a “stuck run threshold” value (storage only; no new auto-remediation behavior).
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 runner) Target Platform: Web app (Filament admin panel) Project Type: Laravel monolith (Filament pages + services + jobs) Performance Goals: Settings resolution should remain request-local cached (no repeated DB reads per key within a request). Constraints:
- DB-only rendering for settings UI (no Graph calls as a render side-effect)
- Strict workspace isolation (non-member 404)
- Capability-gated mutations (member without capability 403)
- Destructive-like resets require confirmation
- Audit each successful mutation; multi-key save produces one audit entry per key changed
Baselines to Preserve (When Unset)
These are the “no settings configured” behaviors that must remain unchanged and covered by regression tests.
Record the baseline values explicitly so “no change” remains mechanically verifiable over time:
- Backup retention default:
- When a schedule has no
retention_keep_last, the job resolvesbackup.retention_keep_last_defaultand falls back to30if unresolved/non-numeric. - Current clamp behavior: values < 1 are clamped up to 1.
- Source:
app/Jobs/ApplyBackupScheduleRetentionJob.php. - Current system default:
backup.retention_keep_last_defaultis30inapp/Support/Settings/SettingsRegistry.php.
- When a schedule has no
- Drift default severity:
- Drift findings currently default to
Finding::SEVERITY_MEDIUM. - Source:
app/Services/Drift/DriftFindingGenerator.php.
- Drift findings currently default to
- Operation run pruning:
- Prune job default retention is
90days (new PruneOldOperationRunsJob()with default constructor argument). - Source:
app/Jobs/PruneOldOperationRunsJob.phpand schedule inroutes/console.php.
- Prune job default retention is
- “Stuck run threshold”:
- No baseline behavior exists today; for this feature it remains storage-only (must not introduce auto-remediation).
Constitution Check
GATE: Must pass before implementation. Re-check after design/edits.
- DB-only rendering: PASS (Workspace Settings UI is DB-only).
- Graph contract path: PASS (no Graph calls introduced).
- RBAC-UX semantics: PASS-BY-DESIGN (non-member 404; member missing capability 403; server-side still authoritative).
- Destructive-like confirmation: PASS-BY-DESIGN (per-setting reset actions must require confirmation).
- Auditability: PASS-BY-DESIGN (settings writes are audited per key).
- Filament Action Surface Contract: PASS (page-level action surface is explicitly declared in spec via UI Action Matrix).
Project Structure
Documentation (this feature)
specs/098-settings-slices-v1-backup-drift-ops/
├── plan.md
├── spec.md
├── tasks.md
└── checklists/
└── requirements.md
Source Code (repository root)
app/
├── Filament/Pages/Settings/WorkspaceSettings.php
├── Jobs/
│ ├── ApplyBackupScheduleRetentionJob.php
│ └── PruneOldOperationRunsJob.php
├── Models/Finding.php
├── Services/Drift/DriftFindingGenerator.php
└── Support/
├── Badges/Domains/FindingSeverityBadge.php
└── Settings/SettingsRegistry.php
routes/console.php
tests/Feature/
├── BackupScheduling/BackupScheduleLifecycleTest.php
├── Drift/DriftPolicySnapshotDriftDetectionTest.php
├── Scheduling/PruneOldOperationRunsScheduleTest.php
└── SettingsFoundation/
├── WorkspaceSettingsManageTest.php
├── WorkspaceSettingsViewOnlyTest.php
└── WorkspaceSettingsNonMemberNotFoundTest.php
Structure Decision: Use existing Laravel structure only. No new top-level directories.
Plan Phases
Phase 0 — Align the Registry + UI primitives (shared)
- Ensure Settings registry rules match the spec (notably the backup max bounds).
- Refactor the Workspace Settings page to support:
- Per-setting reset actions (no global reset)
- Unset inputs with helper text showing the effective/default value
- Save semantics that can “unset” (delete override) for a single key
- Update the existing SettingsFoundation tests to reflect the new UX primitives.
Phase 1 — US1 Backup slice (MVP)
- Add
backup.retention_min_floorto the registry and UI. - Apply floor clamping in the retention job to both schedule overrides and workspace defaults.
- Add/extend BackupScheduling + SettingsFoundation tests to cover both baseline behavior and clamping.
Phase 2 — US2 Drift slice
- Add
drift.severity_mappingto the registry and UI with strict JSON shape validation. - Normalize severity values to lowercase on save; reject unsupported severities.
- Apply mapping in drift finding generation with default
mediumfallback. - Add drift tests for default + mapped severity, and settings validation tests.
Phase 3 — US3 Operations slice
- Add operations keys to the registry and UI.
- Wire pruning job to use the configured retention days when set (baseline otherwise).
- Ensure “stuck threshold” is stored only (no new behavior in this feature).
- Add pruning job/schedule tests and settings persistence tests.
Phase 4 — Format + focused regression
- Run Pint for touched files.
- Run the focused test set for SettingsFoundation + BackupScheduling + Drift + Scheduling.
Complexity Tracking
No constitution violations are required for this feature.