# Data Model: Empty State Consistency Pass

**Feature**: 122-empty-state-consistency | **Date**: 2026-03-08

## Overview

This feature does not add or modify database tables. It standardizes the UI contract for six existing Filament list surfaces when their datasets are empty.

The feature operates on existing resource/page configuration and existing authorization decisions.

## Existing UI Contract Concepts

### EmptyStateSurface

| Attribute | Type | Notes |
|-----------|------|-------|
| `resource` | string | Filament resource owning the list surface |
| `ownership_scope` | enum | `tenant`, `workspace`, or `workspace-context monitoring` |
| `heading` | string | Specific, contextual title for the empty state |
| `description` | string | Short explanation of why the list is empty and what to do next |
| `icon` | string | Heroicon name matching the module semantics |
| `primary_cta_label` | string | Exactly one user-facing CTA label |
| `primary_cta_type` | enum | `create`, `navigate`, or `queue-operation` |
| `capability_behavior` | enum | `disabled-with-explanation` or `hidden`, preserved per existing resource behavior |
| `definition_source` | enum | Prefer `resource.table`; fallback `list-page helper` only when required |

### EmptyStatePrimaryAction

| Attribute | Type | Notes |
|-----------|------|-------|
| `label` | string | Single guided next-step action |
| `target` | string | Destination page or mounted Filament action |
| `enforcement_helper` | string | Existing `UiEnforcement` / `WorkspaceUiEnforcement` pattern when applicable |
| `capability` | string/null | Existing canonical capability required to execute the action |
| `header_relocation_rule` | string | Same CTA appears in the table header when records exist, unless the surface has an explicit documented exemption |

### VisualQaEvidence

| Attribute | Type | Notes |
|-----------|------|-------|
| `surface` | string | One of the six in-scope list pages |
| `mode` | enum | `light` or `dark` |
| `artifact_location` | string | PR/review attachment reference; not committed repo data |
| `review_result` | enum | `pass` or `follow-up` |

## In-Scope Surface Instances

### Policy list

| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | Policies have not been synced for the tenant yet |
| Primary CTA type | Queue operation |
| Primary CTA label | `Sync from Intune` |
| Existing capability behavior | Capability-aware action using existing tenant sync enforcement |

### Backup Sets list

| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | No backup sets have been created yet |
| Primary CTA type | Create |
| Primary CTA label | `Create backup set` |
| Existing capability behavior | Preserved from current create action behavior |

### Restore Runs list

| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | No restore workflows have been initiated yet |
| Primary CTA type | Create |
| Primary CTA label | `New restore run` |
| Existing capability behavior | Preserved from current create action behavior |

### Backup Schedules list

| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | No automated backup schedules are configured |
| Primary CTA type | Create |
| Primary CTA label | `New backup schedule` |
| Existing capability behavior | Disabled-with-tooltip for valid members without schedule-manage capability |

### Workspaces list

| Field | Value |
|------|-------|
| Scope | Workspace-owned |
| Empty reason | No active workspaces are available to the member |
| Primary CTA type | Create |
| Primary CTA label | `New workspace` |
| Existing capability behavior | Preserved from current workspace create action behavior |

### Alert Deliveries list

| Field | Value |
|------|-------|
| Scope | Workspace-context monitoring |
| Empty reason | No alert deliveries have occurred yet |
| Primary CTA type | Navigate |
| Primary CTA label | `View alert rules` |
| Existing capability behavior | Read-only list; CTA navigates to the configuration surface most likely to explain the empty history |
| Header relocation rule | Explicit exemption: CTA appears only while the list is empty; non-empty state remains header-action free |

## Relationships

```text
EmptyStateSurface
  -> has one EmptyStatePrimaryAction
  -> belongs to one existing Filament resource list page
  -> is validated by programmatic tests
  -> is visually confirmed by PR/review evidence in light and dark mode
```

## State Transition

```text
[0 records on list page]
  -> render complete empty-state contract
     (icon + heading + description + exactly 1 CTA)
  -> user follows CTA or waits for data-producing event
[records exist]
  -> empty state disappears
  -> primary CTA relocates to the table header when applicable
  -> Alert Deliveries remains header-action free by explicit exemption
  -> populated table behavior remains unchanged
```

## Validation Rules

| Rule | Result |
|------|--------|
| Exactly one empty-state CTA per in-scope list | Required |
| Empty-state CTA must preserve existing authorization behavior | Required |
| Non-members continue to receive 404 semantics | Required |
| Members missing capability continue to get 403 on execution | Required |
| Alert Deliveries CTA is navigational, not mutating | Required |
| No schema, route, or Graph contract changes | Required |
