4.2 KiB
Research: Managed Tenant Onboarding Wizard UI (v2) (069)
This document resolves Phase 0 unknowns and captures the design decisions used by plan.md.
Decision 1 — Scope mapping ("Workspace" vs current app tenant)
Decision: For v2, interpret Workspace scope and Managed Tenant from the spec as the existing Filament tenant model: App\Models\Tenant.
Rationale:
- The current app’s authorization plane, routing, and multi-tenancy are already built around
Tenant. - Provider connections are already tenant-scoped (
provider_connections.tenant_id). - Introducing a new Workspace/ManagedTenant hierarchy would expand the change surface (routing, policies, membership model, UI) beyond what is needed to ship the wizard + task board.
Alternatives considered:
- Add a new
workspacesmodel/table withmanaged_tenantsunderneath it.- Rejected for Phase 1: would require reworking tenancy, membership, policies, and navigation.
Decision 2 — Evidence as the status source-of-truth
Decision: Step/task badges are derived from stored evidence rows; any "status" fields elsewhere are treated as derived cache.
Rationale:
- Matches FR-006 and prevents UI from being dependent on ephemeral job output.
- Enables stable audit/troubleshooting and deterministic rendering (Monitoring pages remain DB-only).
Alternatives considered:
- Cache the current status directly on
Tenant.- Rejected: easy to drift from reality and makes history/audit harder.
Decision 3 — Task catalog representation
Decision: Implement onboarding tasks as a small, explicit catalog (PHP enum or config-backed list) that defines:
task_type(stable key)- prerequisites
- which OperationRun type/job implements the task
- which evidence type(s) it writes
Rationale:
- Keeps task definitions centralized and testable.
- Supports the task board UX (show prerequisites, last status, rerun).
Alternatives considered:
- Store task definitions in the database.
- Rejected for Phase 1: adds complexity/migrations and makes review harder.
Decision 4 — Concurrency + idempotency for task starts
Decision: Enforce "one active run per (managed_tenant_id, task_type)" by:
- Using
OperationRunService::ensureRunWithIdentity(...)with an identity payload that includes at least{ tenant_id, task_type }. - Maintaining/using an active-run unique constraint for the computed identity hash.
Rationale:
- Aligns with the constitution (DB-level active run dedupe) and existing provider start-gate patterns.
- Prevents double-click duplicates and conflicting task runs.
Alternatives considered:
- Application-only mutexes/locks.
- Rejected: insufficient without DB-level protection.
Decision 5 — Collaboration locking model
Decision: Add session-level locking fields to onboarding sessions:
locked_by_user_idlocked_until
Rationale:
- Simple, explainable behavior.
- Supports takeover/handoff and prevents silent overwrites.
Alternatives considered:
- Per-step/per-task fine-grained locks.
- Rejected for Phase 1: higher complexity with limited user value.
Decision 6 — Legacy (v1) onboarding migration path
Decision: Provide a migration bridge that can:
- Create a Provider Connection for a tenant if the legacy
Tenantcredential fields exist. - Move credentials into
provider_credentials(encrypted) and stop rendering the legacy credential fields in the onboarding UI.
Rationale:
- Satisfies FR-021 without requiring a full backfill of historical run output.
- Aligns with the repo’s existing secure credential patterns (
ProviderCredential+CredentialManager).
Alternatives considered:
- Keep using
Tenant.app_client_secretfor v2.- Rejected: conflicts with ProviderConnection-first direction and increases leak risk.
Decision 7 — Filament implementation shape
Decision: Implement wizard + task board as Filament Pages/Actions.
Rationale:
- Matches current admin UI architecture.
- Allows capability-gated actions with consistent RBAC UX patterns.
Alternatives considered:
- Standalone Blade controllers for v2 onboarding.
- Rejected: splits patterns and complicates authorization/testing conventions.