# 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 → severity` mapping (default `medium`, 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 resolves `backup.retention_keep_last_default` and falls back to `30` if 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_default` is `30` in `app/Support/Settings/SettingsRegistry.php`.
- Drift default severity:
    - Drift findings currently default to `Finding::SEVERITY_MEDIUM`.
    - Source: `app/Services/Drift/DriftFindingGenerator.php`.
- Operation run pruning:
    - Prune job default retention is `90` days (`new PruneOldOperationRunsJob()` with default constructor argument).
    - Source: `app/Jobs/PruneOldOperationRunsJob.php` and schedule in `routes/console.php`.
- “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)

```text
specs/098-settings-slices-v1-backup-drift-ops/
├── plan.md
├── spec.md
├── tasks.md
└── checklists/
    └── requirements.md
```

### Source Code (repository root)

```text
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_floor` to 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_mapping` to 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 `medium` fallback.
- 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.
