TenantAtlas/specs/240-tenant-onboarding-readiness/data-model.md
Ahmed Darrazi 4706e52616
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 58s
feat: add onboarding readiness workflow
2026-04-25 23:16:00 +02:00

140 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Model — Self-Service Tenant Onboarding & Connection Readiness
**Spec**: [spec.md](spec.md)
No new persistent tables are required for this slice. Readiness is computed at render time from existing onboarding, provider connection, verification, and permission-posture truth.
## Entities
### TenantOnboardingSession (`managed_tenant_onboarding_sessions`)
**Purpose**: Workspace-scoped onboarding workflow record that owns resumability, checkpoint progression, and links to the managed tenant once identified.
**Key fields (existing)**:
- `workspace_id` (required FK)
- `tenant_id` (nullable FK to `tenants.id` until the tenant is linked)
- `entra_tenant_id`
- `current_step`
- `version`
- `lifecycle_state`
- `current_checkpoint`
- `last_completed_checkpoint`
- `reason_code`
- `blocking_reason_code`
- `completed_at`, `cancelled_at`
- `state` JSON, constrained by `TenantOnboardingSession::STATE_ALLOWED_KEYS`
**Relevant `state` keys (existing)**:
- `tenant_name`
- `primary_domain`
- `provider_connection_id`
- `selected_provider_connection_id`
- `verification_operation_run_id`
- `bootstrap_operation_types`
- `bootstrap_operation_runs`
- `connection_recently_updated`
**Relationships (existing)**:
- Belongs to `Workspace`
- May belong to `Tenant`
- Belongs to `startedByUser`
- Belongs to `updatedByUser`
### ProviderConnection (`provider_connections`)
**Purpose**: Tenant-owned provider access record whose consent, verification, and target-scope state inform onboarding readiness.
**Key fields (existing, relevant)**:
- `workspace_id`
- `tenant_id`
- `provider`
- `display_name`
- `connection_type`
- `is_default`
- `is_enabled`
- `consent_status`
- `verification_status`
- target-scope identity fields consumed by `ProviderConnectionTargetScopeNormalizer`
**Relationships / invariants (existing)**:
- The selected provider connection must belong to the same workspace and tenant as the onboarding draft.
- `ProviderConnectionSurfaceSummary::forConnection()` is the shared source for provider summary wording and contextual identity detail.
### VerificationRunEvidence (`operation_runs`, existing subset)
**Purpose**: Existing supporting evidence for verification and bootstrap readiness, including canonical operation detail links.
**Key fields (existing, relevant)**:
- `workspace_id`
- `tenant_id`
- `type`
- `status`
- `outcome`
- `context.provider_connection_id`
- `context.verification_report`
- `summary_counts`
**Constraints / invariants (existing)**:
- The verification run must belong to the same workspace and tenant as the onboarding draft.
- Verification evidence is only trustworthy for readiness when `context.provider_connection_id` matches the drafts selected provider connection.
- Canonical evidence links must continue to flow through `OperationRunLinks` / tenantless operation helpers.
### PermissionPostureOverview (derived from existing permission posture data)
**Purpose**: Existing stored permission comparison summary used by onboarding verification assist and readiness freshness cues.
**Source (existing)**:
- `TenantPermissionService::compare(...)`
- `TenantRequiredPermissionsViewModelBuilder::build(...)`
**Relevant derived fields (existing)**:
- `overview.overall`
- `overview.counts.missing_application`
- `overview.counts.missing_delegated`
- `overview.counts.error`
- `overview.freshness.last_refreshed_at`
- `overview.freshness.is_stale`
**Invariant (existing)**:
- Permission freshness is stale when no refresh exists or `last_refreshed_at` is older than 30 days (`TenantRequiredPermissionsViewModelBuilder::deriveFreshness`).
### OnboardingReadinessSummary (computed, not persisted)
**Purpose**: Operator-facing derived summary rendered on the onboarding landing picker and route-bound draft view.
**Proposed runtime shape (presentation-only)**:
- `draft`: `id`, `tenant_name`, `stage_label`, `draft_status_label`, `started_by`, `updated_by`, `last_updated_human`
- `checkpoint`: `current_checkpoint`, `last_completed_checkpoint`, `lifecycle_state`
- `provider_summary`: `readiness_summary`, `consent_state`, `verification_state`, `target_scope_summary`, `contextual_identity_line`
- `verification`: `status`, `overall`, `run_id`, `run_url`, `is_active`, `matches_selected_connection`
- `freshness`: `connection_recently_updated`, `verification_mismatch`, `permission_last_refreshed_at`, `permission_data_is_stale`
- `blocker`: `reason_code`, `blocking_reason_code`, `operator_summary`
- `next_action`: `label`, `kind`, `url_or_action`, `required_capability`
- `supporting_links`: `operation_url`, `tenant_url`, `consent_url` when already available from existing routes/helpers
**Important rule**: This is a presentation shape only. It must map directly from existing onboarding lifecycle, provider connection, verification, and permission-posture truth. It is not a new domain model or persisted state family.
## Derived Rules / Invariants
- A draft without tenant identity cannot be ready; the primary action remains the identify-tenant step.
- A draft without a selected provider connection cannot be ready; the primary action remains connect/select provider.
- A verification run that does not match the selected provider connection is stale for readiness and must force a non-ready outcome.
- `connection_recently_updated=true` invalidates previous verification trust until verification reruns.
- Stale permission posture (`overview.freshness.is_stale=true`) must surface as a readiness attention cue or diagnostic freshness cue, not as ready.
- Top-level readiness wording stays platform-neutral. Provider-specific permission names and consent instructions remain inside secondary diagnostics.
- Supporting evidence uses canonical operation links only; no page-local run URLs are introduced.
## Rendering Precedence (derived, not persisted)
No new persisted transitions are introduced. The readiness summary should follow this rendering precedence when choosing one primary next action:
1. No identified tenant: `Identify tenant`
2. No selected provider connection: `Connect provider`
3. Consent missing or revoked: `Grant consent`
4. Permission diagnostics blocked or incomplete: `Review permissions` / `Grant consent` as dictated by existing provider-owned diagnostics
5. Verification missing, stale, or mismatched: `Start verification` or `Rerun verification`
6. Verification active: `Open operation` or `Refresh`
7. Bootstrap selected and still active/failed: `Review bootstrap`
8. Lifecycle ready for activation: `Complete onboarding`
The multi-draft landing surface uses the same precedence, but only in compact form so the operator can choose the correct draft to open.