TenantAtlas/specs/073-unified-managed-tenant-onboarding-wizard/plan.md
Ahmed Darrazi 7b0a383182 feat: unified managed tenant onboarding wizard
Implements workspace-scoped managed tenant onboarding wizard (Filament v5 / Livewire v4) with strict RBAC (404/403 semantics), resumable sessions, provider connection selection/creation, verification OperationRun, and optional bootstrap. Removes legacy onboarding entrypoints and adds Pest coverage + spec artifacts (073).
2026-02-03 18:27:39 +01:00

8.8 KiB

Implementation Plan: Unified Managed Tenant Onboarding Wizard (073)

Branch: 073-unified-managed-tenant-onboarding-wizard | Date: 2026-02-03 | Spec: specs/073-unified-managed-tenant-onboarding-wizard/spec.md Input: Feature specification from specs/073-unified-managed-tenant-onboarding-wizard/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.

Summary

Deliver a single, resumable onboarding wizard for Managed Tenants that: (1) identifies/upserts a managed tenant within the current workspace, (2) attaches or configures a Provider Connection, (3) runs verification asynchronously as an OperationRun with sanitized outcomes, and (4) optionally kicks off bootstrap operations.

Implementation approach: reuse existing primitives (App\Models\Tenant, Provider Connections, provider.connection.check operation type, workspace + tenant isolation middleware, canonical capability registries) and replace legacy tenant registration/redirect entry points with a single workspace-scoped wizard route.

Technical Context

Language/Version: PHP 8.4.x (Composer constraint: ^8.2)
Primary Dependencies: Laravel 12, Filament 5, Livewire 4+, Pest 4, Sail 1.x
Storage: PostgreSQL (Sail) + SQLite in tests where applicable
Testing: Pest (via vendor/bin/sail artisan test)
Target Platform: Web app (Sail for local dev; container-based deploy on Linux)
Project Type: Web application (Laravel monolith)
Performance Goals: Onboarding UI renders DB-only; all Graph calls occur in queued work tracked by OperationRun; avoid N+1 via eager loading for any list/detail.
Constraints: Tenant isolation (404 vs 403 semantics); no secret material ever returned to the UI/logs; idempotent run-start and onboarding session resume; destructive-like actions require confirmation.
Scale/Scope: Workspace-scoped onboarding; expected low volume but high correctness/safety requirements.

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

GATE RESULT: PASS (no planned constitution violations).

  • Inventory-first: onboarding writes only tenant metadata + configuration pointers; no inventory/snapshot side effects.
  • Read/write separation: onboarding creates/updates records and starts operations; all mutating actions are authorized, audited, and tested.
  • Graph contract path: verification uses existing GraphClientInterface methods (e.g., getOrganization()), and runs only in queued jobs.
  • Deterministic capabilities: use App\Support\Auth\Capabilities + WorkspaceRoleCapabilityMap; add a dedicated onboarding capability granted to Owner+Manager.
  • RBAC-UX semantics: workspace membership enforced via ensure-workspace-member; tenant membership enforced via EnsureFilamentTenantSelected / DenyNonMemberTenantAccess with deny-as-not-found (404). Missing capability returns 403.
  • Destructive confirmation: any archive/delete/credential-rotation actions involved in onboarding must be ->action(...)->requiresConfirmation().
  • Run observability: verification + optional bootstrap actions start via OperationRun and enqueue only; monitoring pages remain DB-only.
  • Data minimization: onboarding session stores only non-secret fields; run failures store reason codes + sanitized messages.
  • BADGE-001: introduce/extend Managed Tenant status badges via BadgeCatalog domain mapping (no per-page mapping).

Project Structure

Documentation (this feature)

specs/073-unified-managed-tenant-onboarding-wizard/
├── plan.md              # This file (/speckit.plan command output)
├── research.md          # Phase 0 output (/speckit.plan command)
├── data-model.md        # Phase 1 output (/speckit.plan command)
├── quickstart.md        # Phase 1 output (/speckit.plan command)
├── contracts/           # Phase 1 output (/speckit.plan command)
└── tasks.md             # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)

Source Code (repository root)

app/
├── Filament/
│   ├── Pages/
│   │   └── Workspaces/
│   │       ├── ManagedTenantsLanding.php
│   │       └── (new) ManagedTenantOnboardingWizard.php
│   └── Pages/Tenancy/
│       └── RegisterTenant.php   # legacy entry point to remove/disable
├── Http/Controllers/
│   └── TenantOnboardingController.php # legacy admin-consent helper; evaluate usage
├── Jobs/
│   └── ProviderConnectionHealthCheckJob.php # verification via OperationRun
├── Models/
│   ├── Tenant.php
│   ├── ProviderConnection.php
│   └── (new) TenantOnboardingSession.php
└── Services/
  ├── Auth/
  │   ├── WorkspaceCapabilityResolver.php
  │   └── WorkspaceRoleCapabilityMap.php
  ├── Providers/
  │   ├── ProviderOperationRegistry.php
  │   └── ProviderGateway.php
  └── Graph/
    └── GraphClientInterface.php

database/migrations/
├── (new) *_add_workspace_scoped_unique_tenant_id.php
└── (new) *_create_tenant_onboarding_sessions_table.php

routes/web.php

tests/Feature/
└── (new) ManagedTenantOnboardingWizardTest.php

Structure Decision: Laravel web application (monolith). Onboarding wizard is a Filament page mounted on a workspace-scoped route under /admin/w/{workspace}/... (no tenant context required to start).

Complexity Tracking

Fill ONLY if Constitution Check has violations that must be justified

Violation Why Needed Simpler Alternative Rejected Because
[e.g., 4th project] [current need] [why 3 projects insufficient]
[e.g., Repository pattern] [specific problem] [why direct DB access insufficient]

No constitution violations are anticipated for this feature.

Phase 0 — Outline & Research (complete)

Outputs:

  • research.md: decisions + rationale + alternatives (no unresolved clarifications).

Key research conclusions:

  • Reuse App\Models\Tenant as “Managed Tenant” (no new base concept), but introduce pending status for the onboarding lifecycle.
  • Replace legacy onboarding/registration routes (/admin/register-tenant, redirects under /admin/managed-tenants/*) with a single workspace-scoped onboarding wizard.
  • Use existing provider verification operation type (provider.connection.check) executed via ProviderConnectionHealthCheckJob with OperationRun tracking.

Phase 1 — Design & Contracts (complete)

Outputs:

  • data-model.md: entities, fields, relationships, validation, state transitions.
  • contracts/*: documented HTTP routes + action contracts (OpenAPI-style where applicable).
  • quickstart.md: dev notes, env vars, how to run tests.

Design highlights:

  • Data model
    • Tenants: change status lifecycle to include pending, ensure workspace_id is NOT NULL + FK, and enforce global uniqueness of tenant_id (Entra tenant ID) bound to exactly one workspace.
    • Onboarding sessions: new table/model for resumable state (strictly non-secret) keyed by (workspace_id, tenant_id).
  • Authorization
    • Introduce a workspace capability for onboarding (e.g., workspace_managed_tenant.onboard) and map it to Owner+Manager via WorkspaceRoleCapabilityMap.
    • Enforce server-side authorization for every mutation and operation-start; 404 for non-members and cross-workspace access; 403 for members missing capability.
  • Runs
    • Verification is a queued OperationRun using provider.connection.check.
    • Optional bootstrap actions become separate OperationRun types (only if they exist in the ProviderOperationRegistry).

Phase 2 — Implementation Plan (to be executed by /speckit.tasks)

This plan intentionally stops before creating tasks.md.

Proposed sequencing for tasks:

  1. Introduce TenantOnboardingSession model + migration, and add workspace-scoped uniqueness for tenants.
  2. Implement ManagedTenantOnboardingWizard page mounted at /admin/w/{workspace}/managed-tenants/onboarding.
  3. Wire verification start to existing ProviderConnectionHealthCheckJob / provider.connection.check operation.
  4. Remove/disable legacy entry points (RegisterTenant, redirect routes) and ensure “not found” behavior.
  5. Add Pest feature tests for: 404 vs 403 semantics, idempotency, resumability, and sanitized run outcomes.