# Feature Specification: Settings Foundation (Workspace + Optional Tenant Override)

**Feature Branch**: `097-settings-foundation`  
**Created**: 2026-02-15  
**Status**: Draft  
**Input**: User description: "Settings Foundation (Workspace + optional Tenant override)"

## Spec Scope Fields *(mandatory)*

- **Scope**: workspace (with optional tenant overrides for future expansion)
- **Primary Routes**: Admin panel “Settings” page (workspace-scoped)
- **Data Ownership**: Workspace-owned settings records, with optional tenant override records that must belong to the same workspace
- **RBAC**: Workspace membership required (non-members see 404); capability-gated view vs manage (Owner/Manager manage; Operator/Readonly view)

## Clarifications

### Session 2026-02-15

- Q: Which role-to-capability mapping should this spec enforce for workspace settings? → A: Option A (Owner+Manager manage; Operator+Readonly view)
- Q: What should the system default be for `backup.retention_keep_last_default`? → A: Option A (`30`)
- Q: Should `SettingsResolver` implement cross-request caching in v1? → A: Option A (request-local cache only)
- Q: When a manager clicks “Reset to system default”, what should happen to the stored workspace override? → A: Option A (remove the override so resolution falls back to system default)

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Manage workspace settings safely (Priority: P1)

Workspace administrators need a single, consistent place to configure workspace-wide defaults that are used by operational features and can be audited later.

**Why this priority**: Without a settings foundation, features will implement ad-hoc settings patterns that are inconsistent, hard to audit, and risky to operate.

**Independent Test**: A manager updates the pilot setting, sees the new value reflected, and the system uses it as the workspace default for relevant behavior.

**Acceptance Scenarios**:

1. **Given** a workspace member with settings-manage capability, **When** they update the pilot setting value, **Then** the value is persisted for that workspace and an audit entry is recorded.
2. **Given** a workspace member with settings-manage capability and an existing workspace override, **When** they reset the pilot setting to the system default, **Then** the effective value becomes the system default and an audit entry is recorded.

---

### User Story 2 - View settings without the ability to change them (Priority: P2)

Read-only operators need to see what the workspace defaults are, without being able to modify them.

**Why this priority**: Visibility supports troubleshooting and governance without expanding write privileges.

**Independent Test**: A viewer can open the settings page and see values, but cannot persist any changes.

**Acceptance Scenarios**:

1. **Given** a workspace member with settings-view capability but without settings-manage capability, **When** they open the settings page, **Then** they can view the current value but cannot save or reset settings.
2. **Given** a workspace member without settings-manage capability, **When** they attempt to submit a settings change by any means, **Then** the request is rejected with 403 and no setting is changed.

---

### User Story 3 - Tenant overrides take precedence (backend-ready) (Priority: P3)

Some settings may need tenant-specific overrides while still inheriting workspace defaults when no override exists.

**Why this priority**: Establishes a stable precedence model early, so future features can safely add tenant overrides without redefining rules.

**Independent Test**: For a chosen tenant in a workspace, resolving a setting returns tenant override when present, otherwise workspace override, otherwise system default.

**Acceptance Scenarios**:

1. **Given** no stored values for a setting, **When** the system resolves it for a workspace (and optional tenant), **Then** the system default is returned.
2. **Given** a workspace override exists, **When** the system resolves the setting, **Then** the workspace value is returned.
3. **Given** a tenant override exists within the same workspace, **When** the system resolves the setting for that tenant, **Then** the tenant value is returned even if a workspace value exists.

### Edge Cases

- Unknown setting keys: write attempts are rejected and no changes are persisted.
- Invalid values (wrong type / out-of-range): write attempts are rejected and no changes are persisted.
- Tenant/workspace mismatch: the system rejects any attempt to store or resolve a tenant override for a tenant outside the workspace.
- Concurrent updates: the last accepted update wins, and each accepted mutation results in exactly one audit entry.
- Caching: within a single request, repeated reads for the same (workspace, optional tenant, domain, key) do not cause repeated database reads; across requests, no external cache is used in v1.

## Requirements *(mandatory)*

**Constitution alignment (required):** This feature introduces workspace-scoped settings mutations that intentionally do not create an operational run record. Each successful settings mutation MUST be auditable via an audit entry, including who changed what and when.

**Constitution alignment (RBAC-UX):**

- Authorization planes: workspace-scoped admin panel surface.
- 404 vs 403 semantics:
  - non-member / not entitled to the workspace scope → 404 (deny-as-not-found)
  - member but missing manage capability for mutations → 403
- Server-side enforcement: all setting mutations (update/reset) MUST be authorization-checked server-side; UI visibility/disabled states are not sufficient.

**Role mapping (clarified):**

- Owners and Managers MUST have settings-manage capability.
- Operators and Readonly members MUST have settings-view capability.

### Functional Requirements

- **FR-001**: System MUST provide a single settings substrate that can store workspace-wide defaults.
- **FR-002**: System MUST support optional tenant overrides that are always scoped to the same workspace.
- **FR-003**: System MUST resolve setting values using the precedence order: tenant override → workspace override → system default.
- **FR-004**: System MUST reject writes for unknown setting keys.
- **FR-005**: System MUST validate setting values against centrally-defined rules and reject invalid values.
- **FR-006**: System MUST ensure settings data cannot be read or written across workspace boundaries.
- **FR-007**: System MUST record an audit entry for each successful settings update and each successful reset-to-default.
- **FR-008**: System MUST provide a workspace-scoped Settings UI surface that supports viewing and managing settings based on capabilities.
- **FR-009**: System MUST include a pilot setting: `backup.retention_keep_last_default`.
- **FR-010**: The pilot setting MUST define a system default of `30` (preserves current behavior), and it MUST be overrideable at workspace scope.
- **FR-011**: When the pilot setting is used by backup scheduling/retention behavior, per-schedule overrides (if present) MUST continue to take precedence over workspace defaults.
- **FR-012**: The settings resolver MUST implement request-local caching for resolved values within a single request.
- **FR-013**: A reset-to-default MUST remove the stored override for the setting scope so future resolution falls back to the system default.

## UI Action Matrix *(mandatory when Filament is changed)*

| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Settings Page | Admin panel (workspace scope) | Save, Reset to system default | N/A | N/A | N/A | N/A | N/A | N/A | Yes | Reset is destructive-like and requires explicit confirmation; non-members get 404; members without manage get 403 on mutation |

### Key Entities *(include if feature involves data)*

- **Setting Definition**: A centrally-defined (domain, key) entry that declares the system default and validation constraints.
- **Workspace Setting Value**: A stored override for a (domain, key) at workspace scope.
- **Tenant Setting Value**: A stored override for a (domain, key) at tenant scope within the same workspace.
- **Settings Audit Entry**: An immutable record of a settings update or reset, including actor identity, target workspace/tenant, and before/after values.

### Non-Functional Requirements

- **NFR-001 (Security / Isolation)**: Non-members MUST receive deny-as-not-found (404) for workspace settings surfaces.
- **NFR-002 (Authorization)**: Members without the required capability MUST receive 403 for forbidden mutations.
- **NFR-003 (Auditability)**: 100% of successful updates and resets MUST create an audit entry including actor, scope, and before/after values.
- **NFR-004 (Data minimization)**: Audit entries MUST NOT include secrets or sensitive raw payloads.
- **NFR-005 (Performance)**: Within a single request, repeated resolutions of the same setting scope MUST not require repeated database reads.

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: An authorized manager can update the pilot setting, and the Settings UI shows the new effective value after saving and persists it across a page reload.
- **SC-002**: A viewer can open the Settings UI and see current values, and cannot persist changes (mutation attempts are rejected).
- **SC-003**: Non-members cannot discover the Settings UI surface for a workspace (requests return 404).
- **SC-004**: 100% of successful setting updates/resets create an audit entry that includes actor, scope (workspace + optional tenant), and before/after values.
