# Implementation Plan: Empty State Consistency Pass **Branch**: `122-empty-state-consistency` | **Date**: 2026-03-08 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/122-empty-state-consistency/spec.md` **Note**: This plan is generated by the `/speckit.plan` workflow and aligned to the current repository constitution. ## Summary Unify the empty-state experience across six primary Filament list pages so each empty list renders an enterprise-grade icon, heading, description, and exactly one guided CTA. The implementation will prefer resource-level `table()` empty-state definitions, preserve existing capability-aware CTA behavior, reuse local patterns from `BaselineProfileResource` and `ReviewPackResource`, update `AlertDeliveryResource`’s action-surface declaration to remove the empty-state exemption, document Alert Deliveries as the sole explicit UX-001 header-relocation exemption, and extend focused Pest + Livewire coverage for content, CTA outcomes, populated-state regression behavior, RBAC behavior, and action-surface guard compliance. ## Technical Context **Language/Version**: PHP 8.4.15 / Laravel 12 **Primary Dependencies**: Filament v5, Livewire v4.0+, Tailwind CSS v4, Pest v4 **Storage**: PostgreSQL + existing workspace/tenant session context; no schema changes **Testing**: Pest v4 feature and Livewire component tests, plus manual light/dark visual QA evidence attached to PR/review **Target Platform**: Laravel Sail web admin + tenant panels (`/admin`, `/admin/t/{tenant}/...`) in containerized deployment **Project Type**: Laravel monolith / Filament web application **Performance Goals**: No additional remote calls, no new queued work, and no material increase in list-render query cost; empty-state rendering remains a table-configuration concern only **Constraints**: Preserve existing RBAC/UI enforcement, keep populated-table behavior unchanged, use exactly one empty-state CTA per surface, prefer resource `table()` definitions, keep screenshots out of the repo, and treat Alert Deliveries as the only explicit exemption from header CTA relocation once rows exist **Scale/Scope**: 6 list surfaces, 6 resource/page empty-state retrofits, 1 action-surface declaration update for Alert Deliveries, and focused regression tests; no migrations, no new services, no new routes ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - Inventory-first: PASS (feature only affects empty-state presentation on existing inventory/backup/monitoring/workspace lists; no inventory-vs-snapshot semantics change). - Read/write separation: PASS (no new writes or operational flows are introduced; existing mutating/create/start actions keep their current behavior). - Graph contract path: PASS (no Microsoft Graph endpoints, contracts, or client usage change). - Deterministic capabilities: PASS (no new capabilities; existing canonical capability checks and enforcement helpers remain in place). - RBAC-UX: PASS (non-member 404 and member-without-capability 403 semantics remain unchanged; the feature only changes empty-state presentation). - Workspace isolation: PASS (workspace-scoped resources remain workspace-safe; no chooser or context rules are altered). - Destructive confirmation: PASS / N/A (no destructive actions are added or modified). - Global search: PASS / N/A (no global search behavior changes). - Tenant isolation: PASS (tenant-owned lists stay tenant-scoped; Alert Deliveries remains entitlement-filtered in workspace context). - Run observability: PASS / N/A (feature introduces no new long-running, remote, or queued work; the existing policy sync CTA keeps its current operation-run behavior unchanged). - Ops-UX 3-surface feedback: PASS / N/A for new work; preserved for existing queue-operation CTA on policies. - Ops-UX lifecycle: PASS / N/A (no new `OperationRun` lifecycle changes). - Ops-UX summary counts: PASS / N/A (no new `OperationRun` metrics producers). - Ops-UX guards: PASS (existing guards remain relevant; plan includes updating action-surface guard coverage where the declared contract changes). - Ops-UX system runs: PASS / N/A (no system-run behavior changes). - Automation: PASS / N/A (no scheduled or queued logic added). - Data minimization: PASS (no new persisted state or logging payloads). - Badge semantics (BADGE-001): PASS / N/A (no new status-like badges are added; empty-state icons are not governed by BADGE-001). - Filament UI Action Surface Contract: PASS with required implementation update. The six in-scope list surfaces already have action surfaces; the plan must update empty-state declarations/content and remove the current `AlertDeliveryResource` empty-state exemption. - Filament UI UX-001: PASS. The feature directly advances UX-001 by ensuring each in-scope list empty state has a specific title, explanation, and one CTA. ## Project Structure ### Documentation (this feature) ```text specs/122-empty-state-consistency/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── empty-states.openapi.yaml └── tasks.md ``` ### Source Code (repository root) ```text app/ ├── Filament/ │ └── Resources/ │ ├── PolicyResource.php # MODIFY — move complete empty state into table() and keep sync CTA semantics │ ├── BackupSetResource.php # MODIFY — define complete empty state in table() │ ├── RestoreRunResource.php # MODIFY — define complete empty state in table() │ ├── BackupScheduleResource.php # MODIFY — define complete empty state in table() │ ├── AlertDeliveryResource.php # MODIFY — define complete empty state + update actionSurfaceDeclaration() │ ├── PolicyResource/ │ │ └── Pages/ │ │ └── ListPolicies.php # MODIFY — reduce helper-only empty-state responsibility if needed │ ├── BackupSetResource/ │ │ └── Pages/ │ │ └── ListBackupSets.php # MODIFY — align empty-state CTA ownership with resource table() │ ├── RestoreRunResource/ │ │ └── Pages/ │ │ └── ListRestoreRuns.php # MODIFY — align empty-state CTA ownership with resource table() │ ├── BackupScheduleResource/ │ │ └── Pages/ │ │ └── ListBackupSchedules.php # MODIFY — align empty-state CTA ownership with resource table() │ └── Workspaces/ │ ├── WorkspaceResource.php # MODIFY — define complete empty state in table() │ └── Pages/ │ └── ListWorkspaces.php # MODIFY — align empty-state CTA ownership with resource table() tests/ ├── Feature/ │ ├── Filament/ │ │ ├── CreateCtaPlacementTest.php # MODIFY — preserve empty-state/header CTA relocation behavior │ │ ├── Alerts/ │ │ │ └── AlertDeliveryViewerTest.php # MODIFY — extend empty-state / authorization coverage for alert deliveries │ │ └── [new or updated empty-state tests] # ADD/MODIFY — per-resource empty-state content coverage │ ├── BackupScheduling/ │ │ └── BackupScheduleLifecycleAuthorizationTest.php # MODIFY or reference — disabled tooltip behavior remains intact │ ├── PolicySyncStartSurfaceTest.php # MODIFY or reference — policy sync CTA capability behavior remains intact │ └── Guards/ │ └── ActionSurfaceContractTest.php # MODIFY — cover updated AlertDelivery empty-state declaration resources/ └── views/ └── [no new view templates expected] # prefer native Filament table empty-state configuration ``` **Structure Decision**: Keep the work inside the existing Laravel/Filament monolith. The feature is a resource-level UI consistency pass with focused list-page helper cleanup and targeted Pest/Livewire regression coverage; no new base folders, services, routes, or data layer objects are needed. ## Complexity Tracking > No Constitution Check violations. No justifications needed. | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | — | — | — | ## Phase 0 — Research (output: `research.md`) See: [research.md](./research.md) Research goals: - Confirm the preferred source of truth for complete Filament empty-state definitions in this repo. - Confirm how to preserve existing capability-aware CTA behavior without creating RBAC regressions. - Confirm the correct next-step CTA for the read-only Alert Deliveries surface. - Confirm the smallest reliable test strategy using existing Pest + Livewire patterns and existing action-surface guards. ## Phase 1 — Design & Contracts (outputs: `data-model.md`, `contracts/`, `quickstart.md`) See: - [data-model.md](./data-model.md) - [contracts/empty-states.openapi.yaml](./contracts/empty-states.openapi.yaml) - [quickstart.md](./quickstart.md) Design focus: - Model each list empty state as a UI contract over existing resource tables rather than a new domain or persistence object. - Move in-scope surfaces toward resource-level `table()` ownership of empty-state heading, description, icon, and CTA. - Preserve header-vs-empty-state CTA placement behavior already guarded by existing tests. - Update `AlertDeliveryResource` from an empty-state exemption to an explicit guided empty-state declaration while preserving its documented no-header-action exemption once rows exist. - Keep screenshots and dark-mode QA as review artifacts rather than committed documentation files. ## Phase 2 — Implementation Outline (tasks created in `/speckit.tasks`) ### Resource empty-state retrofit - Add or complete `emptyStateHeading()`, `emptyStateDescription()`, `emptyStateIcon()`, and `emptyStateActions()` on the six in-scope list surfaces. - Reuse each surface’s existing primary CTA behavior (`create`, `navigate`, or `queue operation`) rather than inventing new workflows. - Keep copy concise, enterprise-oriented, and module-specific. ### Ownership consolidation - Move empty-state contract ownership into each resource `table()` configuration where Filament supports it. - Reduce or remove list-page helper duplication so copy, icon, and CTA do not drift across files. - Preserve existing empty/header CTA relocation behavior already covered by the repo. ### Contract and guard alignment - Update `AlertDeliveryResource::actionSurfaceDeclaration()` to satisfy `ListEmptyState` rather than exempt it. - Verify the changed surfaces still satisfy the Action Surface Contract and do not require new baseline exemptions. ### Regression protection - Add or extend per-resource empty-state rendering assertions for heading, description, icon-related presence, and CTA labels. - Add explicit CTA outcome coverage so empty-state actions are proven to navigate or queue the intended existing workflows. - Add populated-state regression coverage for representative create-capable surfaces plus the Alert Deliveries header-action exemption. - Extend capability tests so disabled or hidden CTA behavior remains aligned with the current resource-specific model. - Keep response/feature coverage for workspace-context and alert-delivery entitlement behavior unchanged except for the new empty-state guidance. ### Verification - Run focused Pest suites for CTA placement, authorization behavior, alert deliveries, and action-surface guards. - Run Pint on dirty files through Sail. - Capture light/dark visual QA evidence for all affected surfaces in PR/review artifacts. ## Constitution Check (Post-Design) Re-check result: PASS. The design adds no new routes, data model changes, background work, or authorization semantics. It strengthens UX-001 compliance, preserves canonical RBAC/UI-enforcement behavior, and explicitly updates the Alert Deliveries action-surface declaration so the implemented UI stays aligned with the repository’s guard-enforced contract.