## Summary - add derived onboarding readiness to the managed tenant onboarding workflow and multi-draft picker - keep provider-specific permission diagnostics secondary while preserving canonical `Open operation` and existing onboarding action semantics - add spec-kit artifacts for `240-tenant-onboarding-readiness` and align roadmap/spec-candidate planning notes - unify the required-permissions empty state copy to English ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions/RequiredPermissionsEmptyStateTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - browser smoke exercised the onboarding picker, route-bound mismatch readiness state, canonical `Open operation` path, and local fixture cleanup ## Notes - branch includes the generated spec artifacts under `specs/240-tenant-onboarding-readiness/` - temporary browser smoke tenants/drafts/runs were cleaned from the local environment after validation Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #277
6.5 KiB
Data Model — Self-Service Tenant Onboarding & Connection Readiness
Spec: 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 totenants.iduntil the tenant is linked)entra_tenant_idcurrent_stepversionlifecycle_statecurrent_checkpointlast_completed_checkpointreason_codeblocking_reason_codecompleted_at,cancelled_atstateJSON, constrained byTenantOnboardingSession::STATE_ALLOWED_KEYS
Relevant state keys (existing):
tenant_nameprimary_domainprovider_connection_idselected_provider_connection_idverification_operation_run_idbootstrap_operation_typesbootstrap_operation_runsconnection_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_idtenant_idproviderdisplay_nameconnection_typeis_defaultis_enabledconsent_statusverification_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_idtenant_idtypestatusoutcomecontext.provider_connection_idcontext.verification_reportsummary_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_idmatches the draft’s 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.overalloverview.counts.missing_applicationoverview.counts.missing_delegatedoverview.counts.erroroverview.freshness.last_refreshed_atoverview.freshness.is_stale
Invariant (existing):
- Permission freshness is stale when no refresh exists or
last_refreshed_atis 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_humancheckpoint:current_checkpoint,last_completed_checkpoint,lifecycle_stateprovider_summary:readiness_summary,consent_state,verification_state,target_scope_summary,contextual_identity_lineverification:status,overall,run_id,run_url,is_active,matches_selected_connectionfreshness:connection_recently_updated,verification_mismatch,permission_last_refreshed_at,permission_data_is_staleblocker:reason_code,blocking_reason_code,operator_summarynext_action:label,kind,url_or_action,required_capabilitysupporting_links:operation_url,tenant_url,consent_urlwhen 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=trueinvalidates 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:
- No identified tenant:
Identify tenant - No selected provider connection:
Connect provider - Consent missing or revoked:
Grant consent - Permission diagnostics blocked or incomplete:
Review permissions/Grant consentas dictated by existing provider-owned diagnostics - Verification missing, stale, or mismatched:
Start verificationorRerun verification - Verification active:
Open operationorRefresh - Bootstrap selected and still active/failed:
Review bootstrap - 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.