TenantAtlas/specs/069-managed-tenant-onboarding-wizard/research.md
2026-02-01 12:20:09 +01:00

4.7 KiB

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.