7.1 KiB
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.
OperationRunactive dedupe enforced (already handled viaOperationRunService::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)
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)
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.checkas the unified verification run type.- Already used by onboarding (
ManagedTenantOnboardingWizard::startVerification()) and byStartVerification::providerConnectionCheck().
- Already used by onboarding (
- Tenant verification start surfaces (tenant detail and tenant list actions) will be refactored to start/dedupe
provider.connection.checkviaStartVerification, always resolve the tenant's default provider connection, and always offer a canonical “View run” link. StartVerificationAPI 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 theprovider.connection.checkoperation type, usingVerificationReportWriter::write(...).
- Implement stub generation immediately after
- 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.mdrecords 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.checkrun completes with outcomeblocked,context.verification_reportis present and schema-valid.
Contracts
contracts/operation-run-context.provider-connection-check.schema.json- Documents expected
OperationRun.contextkeys for the verification run type.
- Documents expected
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:
- 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. - Add tenant embedded verification viewer:
- Select latest
provider.connection.checkrun attempt for the tenant. - Show DB-only empty state when none exists.
- Show DB-only “in progress” state when active with no report yet.
- Select latest
- Ensure blocked verification runs always store a schema-valid stub report:
- Post-
finalizeBlockedRun()write viaVerificationReportWriterforprovider.connection.check.
- Post-
- 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.
- 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).
- Blocked start produces a completed blocked run with a schema-valid