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

64 lines
4.7 KiB
Markdown

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