# Data Model: Managed Tenant Onboarding Wizard UI (v2) (069) This is a Phase 1 design artifact. It describes the entities needed to implement evidence-driven onboarding, collaboration locks, and task execution history. ## Context / Scope Mapping For v2, this plan maps the spec’s **Workspace / Managed Tenant** concepts to the existing `App\Models\Tenant` model. ## Entities ### OnboardingSession (new) Represents a resumable onboarding state machine and collaboration surface. **Identity** - `tenant_id` (FK → `tenants.id`) + `status != completed` should have at most one “active” session per tenant (policy decision; optional enforcement). **Fields (suggested)** - `id` (bigint) - `tenant_id` (FK → `tenants.id`) - `provider_connection_id` (FK → `provider_connections.id`, nullable) - `status` (string: `draft|in_progress|completed|abandoned`) - `current_step` (smallint: 1–5) - `assigned_to_user_id` (FK → `users.id`, nullable) - `locked_by_user_id` (FK → `users.id`, nullable) - `locked_until` (timestamp, nullable) - `completed_at` (timestamp, nullable) - `metadata` (jsonb; whitelisted UI metadata only) - timestamps **Validation / invariants** - If `provider_connection_id` is set, it must belong to the same `tenant_id`. - `locked_until` should be short-lived (e.g., 5–10 minutes) and renewed by activity. **State transitions** - `draft → in_progress → completed` - `draft|in_progress → abandoned` (explicit action) ### OnboardingEvidence (new) Stores evidence that drives UI statuses and provides historical visibility. **Identity** - Many evidence rows per tenant and per task type. **Fields (suggested)** - `id` (bigint) - `tenant_id` (FK → `tenants.id`) - `onboarding_session_id` (FK → `onboarding_sessions.id`, nullable) - `provider_connection_id` (FK → `provider_connections.id`, nullable) - `task_type` (string; stable key, e.g. `onboarding.permissions.verify`) - `status` (string: `ok|warn|fail|unknown`) - `reason_code` (string, nullable; stable code aligned with RunFailureSanitizer) - `message` (string, nullable; sanitized, never secrets) - `payload` (jsonb; whitelisted + sanitized evidence payload) - `operation_run_id` (FK → `operation_runs.id`, nullable) - `recorded_at` (timestamp) - `recorded_by_user_id` (FK → `users.id`, nullable) - timestamps **Indexes (suggested)** - `(tenant_id, task_type, recorded_at desc)` - `(tenant_id, task_type)` for “latest evidence per type” query patterns **Validation / invariants** - `message` must be sanitized/redacted (no tokens/secrets/PII dumps). - `payload` is not a raw provider response; store only what the UI needs. ### ProviderConnection / ProviderCredential (existing) v2 uses the existing provider connection model: - `provider_connections` is tenant-scoped. - `provider_credentials.payload` is encrypted and must never be rendered. **v2 scope** - `provider_credentials.type = client_secret` only. ### OperationRun (existing) Provider-affecting onboarding tasks must be executed via `OperationRun` + queued jobs. **Concurrency rule** - For onboarding tasks, identity must include at least `{ tenant_id, task_type }` so there is at most one active run per `(tenant, task_type)`. ## Derived Views / Queries - **Latest evidence per task**: `where tenant_id = ? and task_type = ? order by recorded_at desc limit 1`. - **Task history**: `where tenant_id = ? and task_type = ? order by recorded_at desc`. ## Notes - If we later introduce a real Workspace/ManagedTenant hierarchy, `tenant_id` can evolve into `workspace_id`, and the evidence/session tables can add `managed_tenant_id` without changing the core model.