# Feature Specification: Backup/Restore UI Graph-Safety (Phase 1) **Feature Branch**: `feat/048-backup-restore-ui-graph-safety` **Created**: 2026-01-10 **Status**: Draft ## Purpose Ensure Backup/Restore UI follows the same guardrails as Inventory: - UI renders DB-only (no Microsoft Graph calls during render/mount/options/typeahead) - UI still remains usable with safe fallbacks for unresolved external identifiers - Add programmatic guard tests that fail if Graph is touched while rendering This phase is intentionally minimal-change and high-safety. ## Clarifications ### Session 2026-01-10 - Q: Which pages must the fail-hard `GraphClientInterface` guard tests render? → A: Backup Sets index + Restore wizard. - Q: How should `` be formatted in fallback labels? → A: `…` ### Session 2026-01-11 - Q: How should the fail-hard Graph guard tests be structured? → A: Feature tests that `actingAs(...)` then `GET` the Filament pages’ routes. - Q: For the feature tests, should we assert only HTTP 200, or also a stable UI marker? → A: Assert HTTP 200 + a stable page marker string. ## Users - Tenant Admin - MSP Operator (within authorized tenant scope) ## User Stories ### US1 (P1): Backup Sets Index is Graph-safe As a tenant admin, I can open the Backup Sets index page and it renders DB-only (no Graph calls during request/render/mount/options/typeahead). **Maps to**: Scenario 1 ### US2 (P1): Restore Wizard is Graph-safe incl. Group Mapping UI As a tenant admin, I can open the Restore wizard page and the group mapping UI remains usable without any Graph calls (including typeahead/search/label resolution). **Maps to**: Scenario 2 + Scenario 3 ## User Scenarios & Testing ### Scenario 1: Open Backup pages without Graph access - Given a user is authenticated and has access to a tenant - When they open the Backup Sets index page - Then the page renders successfully (HTTP 200) without any Graph calls ### Scenario 2: Open Restore Wizard without Graph access - Given a user is authenticated and has access to a tenant - When they open the Restore Run wizard page - Then the wizard renders successfully (HTTP 200) without any Graph calls ### Scenario 3: Group mapping shows safe fallback labels - Given the UI displays group IDs (e.g., in mapping fields) - When the UI cannot resolve group names without Graph - Then it shows safe fallback labels like: - `Unresolved (…)` - `Group (external): …` ## Stable Page Marker Rules (for Guard Tests) Guard tests MUST assert a stable, static marker string in addition to HTTP 200. Marker selection rules: - MUST be static UI text that is not tenant data (avoid names, IDs, timestamps, counts) - SHOULD be a column label, section heading, or primary action label rendered by Filament - MUST be present on the initial GET without requiring any Livewire interaction - MUST NOT depend on Microsoft Graph availability Markers MUST be recorded in quickstart.md once chosen. ## Functional Requirements - FR-001: Backup/Restore UI MUST NOT call Microsoft Graph during render/mount/options/typeahead. - FR-002: The Restore Wizard group mapping controls MUST NOT call Graph for search results or label resolution. ### Group Mapping UX (DB-only) When group mapping is required, the UI MUST remain Graph-free and MUST provide a DB-only safe input. - For each unresolved source group, the UI shows a mapping row with: - Source label: ` (…)` - Target input: - Allowed values: - `SKIP` (skip assignment) - A manually entered Entra ID group objectId (GUID/UUID string) - Validation rules: - If not `SKIP`, the value MUST be a syntactically valid UUID - No network lookup/verification is performed in Phase 1 (Graph-free). Existence is validated later during preview/restore execution paths. - Helper text MUST explain: - “Paste the target Entra ID group Object ID (GUID). Names are not resolved in this phase.” - “Use SKIP to omit the assignment.” - FR-003: When names cannot be resolved DB-only, the UI MUST show safe fallback labels using masked identifiers. - `` format: `…` (last 8 characters of the external identifier, prefixed with an ellipsis) - FR-004: Add fail-hard Pest feature tests binding `GraphClientInterface` to throw on any invocation and verify: - Backup Sets index renders OK (HTTP 200 + stable page marker) - Restore wizard renders OK (HTTP 200 + stable page marker) ## Non-Functional Requirements - NFR-001: No new Graph calls are introduced in UI code paths. - NFR-002: No new tables are added in this phase. - NFR-003: Changes should be low-risk and minimal surface area. ## Out of Scope - Moving action handlers (snapshot capture, backup create, restore rerun, restore dry-run) to jobs. - Creating new cache/inventory tables to support group search. ## Success Criteria - SC-001: Guard tests reliably fail if any Backup/Restore UI render path touches Graph. - SC-002: Backup and Restore wizard pages render successfully with Graph disabled. - SC-003: Group mapping UI remains usable with DB-only fallback labels. ## Related Specs - Follow-up (Phase 2): Backup/Restore job orchestration (TBD)