TenantAtlas/specs/084-verification-surfaces-unification/plan.md
ahmido 0e2adeab71 feat(verification): unify verification surfaces (Spec 084) (#102)
Implements Spec 084 (verification-surfaces-unification).

Highlights
- Unifies tenant + onboarding verification start on `provider.connection.check` (OperationRun-based, enqueue-only).
- Ensures completed blocked runs persist a schema-valid `context.verification_report` stub (DB-only viewers never show “unavailable”).
- Adds tenant embedded verification report widget with DB-only rendering + canonical tenantless “View run” links.
- Enforces 404/403 semantics for tenantless run viewing (workspace membership + tenant entitlement required; otherwise 404).
- Fixes admin panel widgets to resolve tenant from record context so Owners can start verification and recent operations renders correctly.

Tests
- Ran: `vendor/bin/sail artisan test --compact tests/Feature/Verification/ tests/Feature/ProviderConnections/ProviderOperationBlockedGuidanceSpec081Test.php tests/Feature/Onboarding/OnboardingVerificationTest.php tests/Feature/RunAuthorizationTenantIsolationTest.php tests/Feature/Filament/TenantVerificationReportWidgetTest.php tests/Feature/Filament/RecentOperationsSummaryWidgetTest.php`

Notes
- Filament v5 / Livewire v4 compatible.
- No new assets; no changes to provider registration.

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #102
2026-02-09 11:28:09 +00:00

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.
  • 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)

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.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).