93 lines
3.6 KiB
Markdown
93 lines
3.6 KiB
Markdown
# 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.
|