# Feature Specification: Verification Surfaces Unification **Feature Branch**: `084-verification-surfaces-unification` **Created**: 2026-02-09 **Status**: Draft **Input**: User description: "Unify tenant + onboarding verification so all verification uses the same run-based mechanism with DB-only viewing and always-available reports (including blocked)." ## Clarifications ### Session 2026-02-09 - Q: For tenant members who are in-scope but lack the capability to start verification, how should the “Verify configuration” action behave in the UI? → A: Visible but disabled, with helper text explaining the missing capability (server still enforces 403 if invoked). - Q: When rendering the embedded verification viewer on the tenant detail page, which run should be selected as “the last relevant run”? → A: Latest run attempt for that tenant+type (even if queued/running); if active and no report yet, show a DB-only “in progress” state. - Q: On the tenant detail page, when no verification run exists yet, what should the embedded verification section do? → A: Show a DB-only empty state with a “Start verification” CTA. - Q: For the canonical tenantless run viewer (/admin/operations/{run}), what should be required to view a run that is associated with a tenant? → A: Require both workspace membership AND tenant entitlement; missing either is deny-as-not-found (404). ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Verify tenant configuration consistently (Priority: P1) As a workspace member with the appropriate permission, I can start a tenant verification from the tenant detail view and immediately see the latest stored verification results, without the page performing external provider calls during rendering. **Why this priority**: This is the primary operational entry-point and must be fast, safe, and predictable. **Independent Test**: Can be fully tested by starting verification from the tenant detail view, asserting a run record exists, and asserting the embedded viewer renders using stored data. **Acceptance Scenarios**: 1. **Given** a tenant with an eligible provider connection, **When** I click “Verify configuration”, **Then** a verification run is started or deduped, and the tenant page renders the report viewer using stored data only. 2. **Given** a tenant where verification cannot proceed (e.g., missing consent/credentials), **When** I click “Verify configuration”, **Then** a completed “blocked” run exists and the embedded viewer shows a stub/preflight report (not an “unavailable” state). ### User Story 2 - Onboarding verify access behaves identically (Priority: P2) As a workspace member with onboarding verification capability, I can start onboarding verification and receive the same run, dedupe/busy, blocked-report, and canonical link behavior as the tenant verification surfaces. **Why this priority**: Removes confusion and reduces operational variance between onboarding and day-2 operations. **Independent Test**: Can be tested by starting verification in onboarding and asserting identical run outcomes and viewer behavior to the tenant surface. **Acceptance Scenarios**: 1. **Given** onboarding verification is started while another verification run is already active for the same target, **When** I start verification again, **Then** I receive a busy/deduped result that points to the existing active run. --- ### User Story 3 - Use canonical run links everywhere (Priority: P3) As a workspace member, I can open the canonical run viewer link from any verification surface and it consistently resolves to the same “tenantless” run route. **Why this priority**: Improves supportability and prevents broken/ambiguous deep links. **Independent Test**: Can be tested by starting verification, then asserting all “View run” links point to the canonical run route and the page is accessible only to authorized members. **Acceptance Scenarios**: 1. **Given** a verification run exists, **When** I click “View run”, **Then** I am taken to the canonical run viewer route for that run. --- ### Edge Cases - Verification is started while an existing run is queued/running for the same target (dedupe/busy behavior must be consistent across surfaces). - Verification cannot proceed due to missing consent/credentials (a completed blocked run must still have a schema-valid report). - Viewer is opened by a non-member (deny-as-not-found behavior). - Stored report data is present but incomplete/invalid (viewer must fail safe with a clear non-leaky message and no external calls). ## Non-Functional Requirements - **NFR-001 (DB-only render)**: Tenant detail, onboarding verification display, and canonical run viewer rendering MUST be DB-only, with no external provider or Graph calls during mount/render/poll/refresh interactions. - **NFR-002 (start path latency)**: Verification start interactions (`started`, `deduped`, `scope_busy`, `blocked`) SHOULD complete request handling in under 1 second under normal local/staging conditions because they only authorize, create/dedupe run state, enqueue, and notify. - **NFR-003 (refresh/polling discipline)**: Verification UI refresh behavior MUST read persisted `OperationRun` state only and MUST NOT trigger inventory refresh or Graph permission reconciliation during display refresh. ## Requirements *(mandatory)* **Constitution alignment (required):** If this feature introduces any Microsoft Graph calls, any write/change behavior, or any long-running/queued/scheduled work, the spec MUST describe contract registry updates, safety gates (preview/confirmation/audit), tenant isolation, run observability (`OperationRun` type/identity/visibility), and tests. If security-relevant DB-only actions intentionally skip `OperationRun`, the spec MUST describe `AuditLog` entries. **Constitution alignment (RBAC-UX):** If this feature introduces or changes authorization behavior, the spec MUST: - state which authorization plane(s) are involved (tenant `/admin/t/{tenant}` vs platform `/system`), - ensure any cross-plane access is deny-as-not-found (404), - explicitly define 404 vs 403 semantics: - non-member / not entitled to tenant scope → 404 (deny-as-not-found) - member but missing capability → 403 - describe how authorization is enforced server-side (Gates/Policies) for every mutation/operation-start/credential change, - reference the canonical capability registry (no raw capability strings; no role-string checks in feature code), - ensure global search is tenant-scoped and non-member-safe (no hints; inaccessible results treated as 404 semantics), - ensure destructive-like actions require confirmation (`->requiresConfirmation()`), - include at least one positive and one negative authorization test, and note any RBAC regression tests added/updated. **Constitution alignment (OPS-EX-AUTH-001):** OIDC/SAML login handshakes may perform synchronous outbound HTTP (e.g., token exchange) on `/auth/*` endpoints without an `OperationRun`. This MUST NOT be used for Monitoring/Operations pages. **Constitution alignment (BADGE-001):** If this feature changes status-like badges (status/outcome/severity/risk/availability/boolean), the spec MUST describe how badge semantics stay centralized (no ad-hoc mappings) and which tests cover any new/changed values. **Constitution alignment (Filament Action Surfaces):** If this feature adds or modifies any Filament Resource / RelationManager / Page, the spec MUST include a “UI Action Matrix” (see below) and explicitly state whether the Action Surface Contract is satisfied. If the contract is not satisfied, the spec MUST include an explicit exemption with rationale. ### Functional Requirements - **FR-001**: System MUST provide a single, unified verification start mechanism used by both the tenant detail “Verify configuration” and onboarding “Verify access” surfaces. - **FR-002**: Starting verification MUST create (or dedupe to) a single “verification run” record for the target, and surface a stable link to view that run. - **FR-003**: When a verification run cannot proceed due to missing prerequisites, the system MUST finalize the run as completed “blocked” and persist a schema-valid stub/preflight verification report. - **DB-only render invariant**: Verification viewers are read-only projections of persisted run/report data; they MUST NOT perform provider/Graph calls and MUST NOT persist permission inventory updates. - **FR-004**: For any verification run that is completed (including blocked), the embedded/onboarding viewers MUST render the verification report using stored data only. - **FR-004a**: The tenant detail embedded viewer MUST select the latest verification run attempt for the tenant and verification type; if that run is active (queued/running) and no report is yet available, the UI MUST render a DB-only “in progress” state. - **FR-004b**: If no verification run exists yet for the tenant and verification type, the tenant detail embedded section MUST show a DB-only empty state with a “Start verification” CTA. - **FR-005**: UI page rendering (including mount/load/summary components) MUST NOT trigger external provider calls directly or indirectly. - **FR-006**: Dedupe rules MUST ensure at most one active run (queued/running) exists per target and verification type; repeated starts during an active run MUST return a busy/deduped outcome. - **FR-007**: The system MUST persist any permissions inventory updates only as part of the verification job’s execution, and MUST NOT persist these updates during page rendering. - **FR-008**: All “View run” links exposed by verification surfaces MUST use the canonical tenantless run viewer route. - **FR-009**: Authorization MUST be enforced server-side: - missing workspace membership OR missing tenant entitlement MUST be deny-as-not-found (404) for tenant-scoped routes/actions and tenantless canonical views of tenant-associated records, - members lacking the required capability to start verification MUST see the action visible-but-disabled with helper text, and MUST receive a forbidden response (403) if invoked. - **FR-010**: The system MUST emit run observability sufficient for operations (run type, outcome, timestamps, target scope) and MUST be test-covered. ### Assumptions & Dependencies - This change unifies surfaces and report availability; it does not expand the set of verification checks beyond what is already produced today. - For tenant verification surfaces (`ViewTenant` header action, tenant embedded CTA, and tenant list verify action), “eligible provider connection” means the resolved **default** provider connection for provider `microsoft`. - Onboarding verification continues to use the session-selected provider connection, and still runs through the same unified operation type and run orchestration. - Verification reports are stored with the verification run record and are treated as the sole source for UI rendering. - External provider calls are permitted only as part of explicit user-triggered verification runs and their execution (never during page rendering). - Existing authorization capabilities and membership rules remain the source of truth; this feature standardizes how they apply across surfaces. ## UI Action Matrix *(mandatory when Filament is changed)* If this feature adds/modifies any Filament Resource / RelationManager / Page, fill out the matrix below. For each surface, list the exact action labels, whether they are destructive (confirmation? typed confirmation?), RBAC gating (capability + enforcement helper), and whether the mutation writes an audit log. | 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 | |---|---|---|---|---|---|---|---|---|---|---| | Tenant detail view | Tenant admin area | Verify configuration | Embedded viewer + View run link | n/a | n/a | Start verification (empty state) | n/a | n/a | Yes | DB-only render; starts/dupes run; if missing capability: visible but disabled with helper | | Tenant list (Tenants table) | Tenant admin area | n/a | Clickable row + View action | Verify configuration (grouped action) | grouped | n/a | n/a | n/a | Yes | Uses same unified start path and canonical run links as tenant detail | | Onboarding verification step | Onboarding wizard | Start verification | Embedded viewer + View run link | n/a | n/a | n/a | n/a | n/a | Yes | Same semantics as tenant surface | | Tenantless run viewer | Operations area | n/a | n/a | n/a | n/a | n/a | n/a | n/a | Yes | Requires workspace membership + tenant entitlement; otherwise 404 | ### Key Entities *(include if feature involves data)* - **Verification Run**: An immutable operational record representing one attempt to verify access/configuration for a target scope. It captures status/outcome and a canonical link for viewing. - **Verification Report**: A schema-valid, stored report attached to a verification run. It is always present for completed runs, including blocked runs (stub/preflight). ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: 100% of completed blocked verification runs display a usable stub report (no "report unavailable" state for completed blocked runs). - **SC-002**: Tenant detail pages render without any external provider calls; verification-related external calls occur only after an explicit start action. - **SC-003**: When a verification run is already active for the same target and type, repeated starts return a busy/deduped response in under 1 second. - **SC-004**: All verification surfaces provide a canonical "View run" link, and support can use that single URL to review outcomes.