# Feature Specification: Empty State Consistency Pass

**Feature Branch**: `122-empty-state-consistency`  
**Created**: 2026-03-08  
**Status**: Ready for Implementation  
**Input**: Spec 122 — Empty State Consistency Pass (Unified empty states across all primary list pages)

## Clarifications

### Session 2026-03-08

- Q: What should the single empty-state CTA be for the read-only Alert Deliveries list? → A: Use “View alert rules” as the single CTA.
- Q: How should empty-state CTA visibility behave for members who lack the required capability? → A: Preserve existing per-resource behavior (disabled or hidden), but require explanation when shown disabled.
- Q: Where should the final empty-state configuration live? → A: Prefer resource `table()` definitions for empty-state heading/description/icon/action, and allow page helpers only when technically required.
- Q: How should screenshots and visual QA evidence be handled for this feature? → A: Attach screenshots / visual QA evidence to the PR or review, not as committed repo artifacts.

## Spec Scope Fields *(mandatory)*

- **Scope**: workspace
- **Primary Routes**:
  - Admin UI list pages: Policies, Backup Sets, Restore Runs, Backup Schedules, Workspaces, Alert Deliveries
- **Data Ownership**:
  - Tenant-owned lists: Policies, Backup Sets, Restore Runs, Backup Schedules
  - Workspace-owned / workspace-context lists: Workspaces
  - Workspace-context monitoring history list: Alert Deliveries (may include tenant-bound rows; tenant entitlement still applies)
- **RBAC**:
  - Membership isolation: non-members are deny-as-not-found (404 semantics)
  - Capability gating: members without the required capability receive authorization denial (403 on execution); UI may show disabled actions with explanatory tooltip
  - Empty-state primary actions MUST remain capability-aware and MUST use the existing UI enforcement helpers already used by each module

## User Scenarios & Testing *(mandatory)*

### User Story 1 - First-run clarity on empty lists (Priority: P1)

As a first-time admin, I want to immediately understand what each list page represents when it has no data yet, and what my next best action is.

**Why this priority**: This is the first impression for new tenants/workspaces and for demos.

**Independent Test**: For each in-scope list page, with zero records, the page renders a complete empty state (icon + heading + description + one primary action).

**Acceptance Scenarios**:

1. **Given** I open an in-scope list page with no records, **When** the table renders, **Then** I see an icon, heading, description, and a single primary action.
2. **Given** I click the primary empty-state action, **When** I have the required permission, **Then** I am taken to the intended next step (or the intended operation is queued) without errors.

---

### User Story 2 - Enterprise-grade consistency during evaluation (Priority: P2)

As a product evaluator, I want empty pages to feel intentional and guided, with consistent hierarchy and tone.

**Why this priority**: In low-data environments, evaluators spend disproportionate time on empty states.

**Independent Test**: Visual and UI assertions confirm consistent presence and ordering of empty-state elements across all in-scope pages.

**Acceptance Scenarios**:

1. **Given** I visit each in-scope list page with no records, **When** I compare the empty states, **Then** they share consistent structure and enterprise-appropriate wording (no scaffold-like presentation).

---

### User Story 3 - Permission-aware guidance (Priority: P3)

As a workspace/tenant member without a required capability, I want to understand what I’m missing without the UI leaking access outside my membership scope.

**Why this priority**: Capability-aware UX reduces support load and aligns with RBAC-UX.

**Independent Test**: With membership but missing capability, the empty-state action is not actionable and explains why; server-side authorization remains enforced.

**Acceptance Scenarios**:

1. **Given** I am a valid member but lack the capability for the primary action, **When** I view the empty page, **Then** the CTA is disabled (or hidden where that’s the existing pattern) and communicates the missing permission.
2. **Given** I am not entitled to the workspace/tenant scope, **When** I attempt to access the list page, **Then** the app responds as not found (404 semantics).

### Edge Cases

- Empty state must still render correctly in dark mode and light mode.
- Empty state must behave correctly when the tenant context is missing/invalid (records are not shown and the page does not leak data).
- Alert Deliveries list can be empty even when alerts are configured (e.g., no alerts fired yet); copy must set that expectation.

## Requirements *(mandatory)*

**Constitution alignment (required):** This feature introduces no new external integrations and no new background work. It only adjusts how existing list pages communicate “no records” states and which single next-step action is presented.

**Constitution alignment (OPS-UX):** This feature MUST NOT change operation UX semantics. Where an empty-state action already queues an operation (e.g., sync), it continues to follow the existing 3-surface feedback contract and uses the canonical operation UX presenter.

**Constitution alignment (RBAC-UX):** This feature MUST NOT relax authorization. It reuses existing capability gating and server-side authorization. Membership remains deny-as-not-found (404 semantics); missing capability remains 403 on execution.

**Constitution alignment (Filament Action Surfaces):** This feature modifies list empty states and therefore MUST satisfy the Action Surface Contract for the “ListEmptyState” slot for each in-scope resource.

**Constitution alignment (UX-001 — Layout & Information Architecture):** Each in-scope list page’s empty state must have a specific title, an explanation, and exactly 1 CTA.

### Functional Requirements

- **FR-001**: Each in-scope primary list page MUST render a complete empty state when there are zero records.
- **FR-002**: Each empty state MUST include: icon, heading, description, and exactly one primary action.
- **FR-003**: Empty-state copy MUST be contextual to the module and explain both (a) why it’s empty and (b) the next best action.
- **FR-004**: Empty-state primary actions MUST remain capability-aware and MUST respect existing UI enforcement rules.
- **FR-004a**: The feature MUST preserve each resource’s existing CTA visibility model for capability enforcement rather than forcing all empty-state actions into a single visible-disabled or hidden-only pattern.
- **FR-004b**: When an empty-state CTA is shown disabled for a valid member, it MUST explain the missing permission using the resource’s existing helper text or tooltip pattern.
- **FR-005**: Empty-state rendering MUST remain correct in both light and dark mode.
- **FR-006**: Empty-state configuration MUST be defined in a single, consistent place per list page (to prevent drift and scaffold-like inconsistencies).
- **FR-006a**: Where Filament supports it, empty-state heading, description, icon, and action MUST be defined in the resource `table()` configuration.
- **FR-006b**: Page-level empty-state helpers MAY be used only when technically required, and MUST NOT split responsibility in a way that causes drift between heading/description/icon/action definitions.
- **FR-007**: Out-of-scope resources MUST remain unchanged, including: Finding, Entra Group, Policy Version, Operation Run.
- **FR-008**: The Alert Deliveries empty state MUST use a single navigational CTA labeled “View alert rules” to direct operators to the configuration surface most likely to explain or resolve an empty history.
- **FR-008a**: Alert Deliveries is the sole explicit UX-001 relocation exemption for this feature: the `View alert rules` CTA appears only in the empty state and MUST NOT persist as a table-header action once deliveries exist, because the surface remains intentionally read-only and header-action free.

### Module-specific empty-state copy (baseline direction)

- Policies: “No policies synced yet. Sync your first tenant to see Intune policies here.”
- Backup Sets: “No backup sets. Create a backup set to start protecting your configurations.”
- Restore Runs: “No restore runs. Start a restoration from a backup set.”
- Backup Schedules: “No schedules configured. Set up automated backups.”
- Workspaces: “No workspaces. Create your first workspace.”
- Alert Deliveries: “No alert deliveries. Deliveries appear automatically when alert rules fire.”

### Clarified CTA direction

- Alert Deliveries: use a single CTA labeled “View alert rules”.

## 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 |
|---|---|---|---|---|---|---|---|---|---|---|
| Policy list | Admin UI — Policies list page | “Sync from Intune” | View action | View + “More” (as-is) | As-is | “Sync from Intune” | As-is | N/A (read-only details) | No | CTA continues to queue the existing sync operation; no change to operation UX semantics. |
| Backup Sets list | Admin UI — Backup Sets list page | “Create” (existing behavior) | View action | View + “More” | Yes | “Create backup set” | As-is | Create/Edit as-is | Yes (existing audit logger) | Empty state becomes contextual and guided; CTA remains capability gated. |
| Restore Runs list | Admin UI — Restore Runs list page | “Create” (existing behavior) | As-is | As-is | Yes | “New restore run” | As-is | Create wizard as-is | Yes (existing audit logger) | Empty state becomes contextual and guided; CTA remains capability gated. |
| Backup Schedules list | Admin UI — Backup Schedules list page | “New backup schedule” (existing behavior) | Clickable row | View/Edit as-is | Yes | “New backup schedule” | Edit page header as-is | Save/Cancel as-is | No | CTA remains capability-aware; disabled state explains missing permission. |
| Workspaces list | Admin UI — Workspaces list page | “New workspace” (existing behavior) | View action | View/Edit as-is | No | “New workspace” | As-is | Save/Cancel as-is | No | Workspace membership isolation remains deny-as-not-found. |
| Alert Deliveries list | Monitoring — Alert Deliveries list page | None (read-only) | Clickable row | None | None | “View alert rules” (navigation) | View page (read-only) | N/A | No | Adds guided empty state despite read-only list; CTA is a next-step navigation to alert rule configuration, not a mutation. Explicit UX-001 exemption: this CTA does not relocate to the header when records exist. |

### RBAC clarification

- Preserve each resource’s current capability-aware CTA behavior (disabled vs hidden) rather than standardizing all empty-state actions to one pattern.

### Placement clarification

- Prefer resource `table()` definitions for empty-state heading, description, icon, and action; use page helpers only when technically required.

### Visual QA evidence clarification

- Screenshot and dark-mode visual QA evidence for affected resources must be attached to the PR or review record, not committed into the repository.

## Explicit exemptions

- **Alert Deliveries header-action exemption**: Alert Deliveries remains a read-only monitoring history surface with no persistent header action. Its single CTA (`View alert rules`) exists only while the list is empty. Once deliveries exist, the CTA does not relocate to the header; this is an explicit UX-001 exemption for this feature and must remain documented in the PR or review.

## Acceptance Criteria / Definition of Done

1. All in-scope resources render a full empty state when the table has no records.
2. Each empty state includes icon, heading, description, and exactly one primary CTA.
3. Empty-state actions preserve existing RBAC/UI-enforcement behavior.
4. Visual review confirms centered, intentional hierarchy consistent with the baseline reference pattern.
5. Dark mode renders correctly for every affected list page.
6. Populated-table behavior remains unchanged.
7. Screenshot or visual QA evidence for all affected resources is attached to the PR or review artifact set, rather than committed to the repository.

## Testing Requirements

- Feature/UI test per in-scope resource for empty table rendering.
- Feature/UI test coverage for each in-scope CTA outcome so empty-state actions either navigate to the intended next step or queue the intended existing operation.
- Permission test coverage to ensure CTA visibility or disabled state remains aligned with existing resource behavior.
- Manual visual verification in light and dark mode for every affected list page.
- Automated regression coverage for representative populated-table behavior, including CTA relocation for create-capable surfaces and the documented no-header-action exemption for Alert Deliveries.
- Smoke test confirming populated resources still render their standard tables and existing actions.
- PR/review attachment checklist confirming screenshots or equivalent visual QA evidence were captured for all affected resources.

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: 100% of in-scope list pages display icon + heading + description + 1 CTA when empty.
- **SC-002**: In a first-run demo with no records, an evaluator can correctly identify the purpose and next action on each in-scope page without guidance.
- **SC-003**: Users without capability can still understand the required permission from the disabled/hidden state messaging, without any cross-scope leakage.
- **SC-004**: No regressions: populated tables continue to render their existing columns, actions, and behaviors unchanged.
