TenantAtlas/specs/084-verification-surfaces-unification/plan.md

132 lines
7.1 KiB
Markdown

# Implementation Plan: Verification Surfaces Unification
**Branch**: `084-verification-surfaces-unification` | **Date**: 2026-02-09 | **Spec**: `specs/084-verification-surfaces-unification/spec.md`
**Input**: Feature specification from `specs/084-verification-surfaces-unification/spec.md`
## Summary
Unify tenant “Verify configuration” and onboarding “Verify access” to the same `OperationRun`-based flow (`provider.connection.check`), with DB-only viewing and canonical tenantless run links. Ensure any **completed blocked** verification run persists a **schema-valid** `context.verification_report` stub so viewers never show “report unavailable” for blocked completions.
## Technical Context
**Language/Version**: PHP 8.4 (Laravel 12)
**Primary Dependencies**: Filament v5 (Livewire v4), Queue/Jobs (Laravel), Microsoft Graph via `GraphClientInterface`
**Storage**: PostgreSQL (JSONB-backed `OperationRun.context`)
**Testing**: Pest v4 (Feature tests), Filament/Livewire component testing where applicable
**Target Platform**: Web application (Sail-first local dev)
**Performance Goals**:
- Tenant detail + onboarding verification surfaces render DB-only with no external provider/Graph calls.
- Start action returns quickly (authorize → create/dedupe run → enqueue job → notify + “View run”).
**Constraints**:
- RBAC isolation: non-members are deny-as-not-found (404); members missing capability are 403 on execution.
- `OperationRun` active dedupe enforced (already handled via `OperationRunService::ensureRunWithIdentity()` + active-run checks).
**Scale/Scope**: Tenant-scoped verification for provider connection health + permission inventory refresh (existing behavior).
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first / Read-write separation: PASS (verification is an explicit user-triggered operation; viewing is read-only).
- Graph contract path: PASS (provider verification uses `ProviderGateway` + `GraphClientInterface`; no render-time Graph calls).
- Workspace/Tenant isolation: PASS (tenantless canonical views must still enforce workspace + tenant entitlement; missing either is 404).
- RBAC-UX 404/403 split: PASS (start is 403 for members missing capability; non-members 404; Livewire calls included).
- Run observability: PASS (verification is queued and tracked as `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only).
- Data minimization/safe logging: PASS (verification report stored in `OperationRun.context`; no secrets; next steps are link-only).
- Filament action safety: PASS (verification start uses `->action(...)`; any destructive action confirmations remain required).
No constitution violations are required for this feature.
## Project Structure
### Documentation (this feature)
```text
specs/084-verification-surfaces-unification/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
└── contracts/
├── operation-run-context.provider-connection-check.schema.json
└── verification-surfaces.routes.md
```
### Source Code (existing, relevant)
```text
app/
├── Filament/
│ ├── Resources/
│ │ ├── TenantResource.php
│ │ └── TenantResource/Pages/ViewTenant.php
│ ├── Pages/Workspaces/ManagedTenantOnboardingWizard.php
│ └── Support/VerificationReportViewer.php
├── Jobs/ProviderConnectionHealthCheckJob.php
├── Services/
│ ├── Providers/ProviderOperationStartGate.php
│ ├── OperationRunService.php
│ └── Verification/StartVerification.php
└── Support/
├── OperationRunLinks.php
└── Verification/VerificationReportWriter.php
resources/views/filament/components/verification-report-viewer.blade.php
resources/views/filament/forms/components/managed-tenant-onboarding-verification-report.blade.php
```
## Phase 0 — Outline & Research
### Key decisions (grounded in current code)
- Use `provider.connection.check` as the unified verification run type.
- Already used by onboarding (`ManagedTenantOnboardingWizard::startVerification()`) and by `StartVerification::providerConnectionCheck()`.
- Tenant verification start surfaces (tenant detail and tenant list actions) will be refactored to start/dedupe `provider.connection.check` via `StartVerification`, always resolve the tenant's default provider connection, and always offer a canonical “View run” link.
- `StartVerification` API changes remain non-breaking in this feature (keep existing explicit-connection method; add a tenant-default start helper rather than replacing signatures).
- Blocked runs must write a schema-valid stub report:
- Implement stub generation immediately after `OperationRunService::finalizeBlockedRun()` for the `provider.connection.check` operation type, using `VerificationReportWriter::write(...)`.
- Tenantless canonical run viewing for tenant-associated runs is a foundational blocker and must enforce workspace membership + tenant entitlement with deny-as-not-found semantics before user-story completion.
### Outputs
- `research.md` records decisions + rationale + alternatives.
## Phase 1 — Design & Contracts
### Data model (no DB migration expected)
- Store verification report exclusively in `operation_runs.context.verification_report` (existing pattern).
- Enforce: when a `provider.connection.check` run completes with outcome `blocked`, `context.verification_report` is present and schema-valid.
### Contracts
- `contracts/operation-run-context.provider-connection-check.schema.json`
- Documents expected `OperationRun.context` keys for the verification run type.
- `contracts/verification-surfaces.routes.md`
- Documents user actions → routes/surfaces and the canonical run viewer URL.
### Outputs
- `data-model.md`, `contracts/*`, `quickstart.md`.
## Phase 2 — Implementation Planning (for `/speckit.tasks`)
Planned work items to convert into `tasks.md`:
1. Refactor tenant verification actions (tenant detail + tenant list) to use the unified start path (`provider.connection.check`) with default connection resolution, returning started/deduped/busy outcomes with a canonical run URL.
2. Add tenant embedded verification viewer:
- Select latest `provider.connection.check` run attempt for the tenant.
- Show DB-only empty state when none exists.
- Show DB-only “in progress” state when active with no report yet.
3. Ensure blocked verification runs always store a schema-valid stub report:
- Post-`finalizeBlockedRun()` write via `VerificationReportWriter` for `provider.connection.check`.
4. Authorization + isolation (blocking):
- Non-members: 404 for tenant routes and tenantless operations viewer of tenant-associated runs.
- Members missing capability: UI visible-but-disabled; server returns 403.
5. Tests (Pest):
- Blocked start produces a completed blocked run with a schema-valid `verification_report`.
- Tenant page and onboarding viewer render from stored report only (no external calls during render).
- Tenant render path never persists permission inventory updates and never uses synchronous verification paths.
- Canonical run links point to `admin.operations.view` (tenantless).