# Research — Managed Tenant Onboarding Wizard v1 This research consolidates repo-specific patterns and decisions needed to implement Spec 069 safely. ## Decision 1 — “Managed tenant” maps to existing `Tenant` model - Decision: Implement the onboarding wizard around the existing `App\Models\Tenant` entity. - Rationale: The current `/admin` panel is already multi-tenant with `Tenant::class` tenancy (`AdminPanelProvider`), tenant membership rules, and tenant-scoped `OperationRun` and provider operations. - Alternatives considered: - Introduce a new `ManagedTenant` model: rejected for v1 because it would duplicate existing tenancy/membership and require broad refactors. ## Decision 2 — Wizard UI implemented as a Filament page using `Step` - Decision: Implement the onboarding flow as a Filament page (tenant-plane) that composes steps using `Filament\Schemas\Components\Wizard\Step`. - Rationale: Repo already uses step-based wizards (`RestoreRunResource`) and Filament v5 + Livewire v4 are the established UI stack. - Alternatives considered: - Keep `TenantResource` simple create/edit forms and add helper text: rejected because Spec 069 requires a guided, resumable multi-step flow. - Build a non-Filament controller + Blade wizard: rejected; would bypass consistent Filament RBAC/UX patterns. ## Decision 3 — RBAC-UX enforcement uses existing middleware + `UiEnforcement` - Decision: Enforce “non-member → 404, member missing capability → 403” via existing infrastructure: - `App\Support\Middleware\DenyNonMemberTenantAccess` for tenant-scoped routes (404 for non-members). - `App\Support\Rbac\UiEnforcement` for Filament actions (disabled + tooltip + 404/403 server-side guards). - `App\Services\Auth\CapabilityResolver` + `App\Support\Auth\Capabilities` registry (no raw strings). - Rationale: This matches the repo constitution and existing patterns in resources/pages. - Alternatives considered: - Ad-hoc `abort(403)` / `abort(404)` scattered in actions: rejected (regression risk; violates RBAC-UX-008 intent). ## Decision 4 — DB-only render is guaranteed by strict separation - Decision: Wizard pages render only from: - `Tenant` fields (including encrypted credential fields that never rehydrate secrets) - onboarding-session persisted payload (JSON) - last completed `OperationRun` records / stored summaries - Rationale: Constitution requires DB-only render for monitoring and operational pages; Livewire requests should not trigger Graph. - Alternatives considered: - “Check on mount”: rejected; would violate DB-only render. ## Decision 5 — All checks are enqueue-only, observable via `OperationRun` - Decision: All verification / connectivity / inventory operations triggered from the wizard create/reuse an `OperationRun` and dispatch a job. - Rationale: `OperationRunService` provides run-identity dedupe with a DB constraint; provider scoped checks already follow this pattern via `ProviderOperationStartGate`. - Alternatives considered: - Synchronous checks in UI actions: rejected; violates run-observability and DB-only render intent. ## Decision 6 — Session persistence uses a dedicated onboarding session table - Decision: Introduce a persisted onboarding session record that stores: - actor + timestamps - current step - non-secret payload JSON - status (active/completed/abandoned) - foreign keys to tenant (once known) - Rationale: Spec requires resumability and dedupe (“auto-resume existing active session”). - Alternatives considered: - Store progress in Laravel session only: rejected (not resilient across devices, logouts, and multi-user concurrency). ## Decision 7 — Capability naming aligns with existing registry - Decision: Use existing canonical capability registry (`App\Support\Auth\Capabilities`) and map Spec 069 semantics to: - start onboarding / create tenant → `Capabilities::TENANT_MANAGE` (or introduce a dedicated `tenant.create` if needed, but still via registry) - manage credentials/config → `Capabilities::TENANT_MANAGE` - run checks (provider operations / inventory) → `Capabilities::PROVIDER_RUN` and/or `Capabilities::TENANT_INVENTORY_SYNC_RUN` - Rationale: Current app already enforces these capabilities widely; adding new strings is possible but must remain centralized. - Alternatives considered: - Introduce `managed_tenants.*` capabilities in parallel: deferred unless Spec 068 v2 requires that rename. ## Open Questions (deferred but not blocking plan) - Whether Spec 068 v2 introduces a separate “Workspace” model and renames `Tenant` to “ManagedTenant”. If yes, the wizard should be adapted in that refactor; the v1 implementation should keep seams (service layer + session model) to migrate cleanly.