# Phase 1 Data Model: Central Tenant Operability Policy ## Overview This feature does not require a new database table in its first implementation slice. The primary data-model work is the formalization of existing persistent records plus new derived domain objects that express tenant operability consistently across lanes. ## Persistent Domain Entities ### Tenant **Purpose**: Durable workspace-owned record whose lifecycle influences operability. **Key fields**: - `id` - `workspace_id` - `external_id` - `status` with canonical values `draft`, `onboarding`, `active`, `archived` - `deleted_at` for archive persistence behavior **Relationships**: - Belongs to one workspace - Has many onboarding sessions, audit logs, provider connections, and tenant-owned operational records - May be referenced by canonical workspace records such as `OperationRun` **Validation rules**: - `workspace_id` must match the active workspace scope for all in-scope route or action decisions - `status` must resolve through the canonical `TenantLifecycle` model - Soft-delete state is an implementation input, never the only semantic rule **State transitions relevant to this feature**: - `draft` → `onboarding` - `onboarding` → `active` - `active` → `archived` - `archived` → `active` ### TenantOnboardingSession **Purpose**: Separate workspace-scoped workflow record used to evaluate onboarding-lane actions. **Key fields**: - `id` - `workspace_id` - `tenant_id` nullable until linked - workflow state and lifecycle fields already used by onboarding services **Relationships**: - Belongs to one workspace - Optionally belongs to one tenant **Validation rules**: - Must be in the same workspace as any linked tenant - Onboarding-specific operability decisions must validate both tenant lifecycle and workflow resumability ### OperationRun **Purpose**: Canonical workspace-owned record that may reference a tenant without becoming subordinate to selected tenant context. **Key fields**: - `id` - `workspace_id` - `tenant_id` nullable - `type` - `status` - `outcome` **Relationships**: - Belongs to one workspace - May belong to one tenant reference **Validation rules**: - Canonical route legitimacy is based on the run and workspace first - Tenant-linked follow-up actions must still respect tenant entitlement and operability outcomes ### UserTenantPreference and WorkspaceContext Session State **Purpose**: Stores remembered tenant context for the active operating lane. **Key fields**: - `user_id` - `tenant_id` - `last_used_at` - session-scoped workspace and remembered-tenant identifiers **Validation rules**: - Remembered tenant is valid only when workspace match, tenant existence, entitlement, and selector-lane operability all still hold ## New Derived Domain Objects ### TenantInteractionLane **Purpose**: Explicitly identifies the lane in which the tenant is being evaluated. **Canonical values**: - `standard_active_operating` - `onboarding_workflow` - `administrative_management` - `canonical_workspace_record` **Why it exists**: - The same tenant can be selector-ineligible yet still administratively viewable or canonically referenceable ### TenantOperabilityContext **Purpose**: Normalized evaluation input for the central policy layer. **Fields**: - `tenant` - `actor` - `workspaceId` - `lane` - `pageCategory` - `linkedRecordType` nullable - `linkedRecordId` nullable - `onboardingDraft` nullable - `requiredCapability` nullable - `selectedTenant` nullable **Context ownership**: - Consumers normalize and pass route, record, workflow, and selected-tenant inputs into the context. - The central service resolves workspace membership, tenant entitlement, capability truth, lifecycle, and archived persistence state through canonical workspace, tenant, and RBAC helpers at evaluation time. - Consumers may request a capability-aware question, but they do not authoritatively decide membership or entitlement before evaluation. **Validation rules**: - `tenant.workspace_id` must equal `workspaceId` - linked records must belong to the same workspace when tenant-linked - `selectedTenant` may be informative but never authoritative ### TenantOperabilityQuestion **Purpose**: Declares the exact semantic question the consumer is asking. **Canonical values**: - `selector_eligibility` - `remembered_context_validity` - `tenant_bound_viewability` - `canonical_linked_record_viewability` - `archive_eligibility` - `restore_eligibility` - `resume_onboarding_eligibility` - `onboarding_completion_eligibility` - `verification_readiness_eligibility` - `administrative_discoverability` ### TenantOperabilityOutcome **Purpose**: Structured result returned by the central operability policy. **Fields**: - `question` - `allowed` - `lifecycle` - `lane` - `reasonCode` nullable - `requiredCapability` nullable - `discoverable` boolean - `informationalMessageKey` nullable - `metadata` key-value bag for consumer-safe hints **Behavior**: - Must be stable enough for unit assertions and UI mapping - May expose helper methods for common adapters, but raw outcome data remains canonical ### TenantOperabilityReasonCode **Purpose**: Stable denial or ineligibility reason catalog. **Initial values**: - `workspace_mismatch` - `tenant_not_entitled` - `missing_capability` - `wrong_lane` - `selector_ineligible_lifecycle` - `tenant_not_archived` - `tenant_already_archived` - `onboarding_not_resumable` - `canonical_view_followup_only` - `remembered_context_stale` ## Consumer Mapping | Consumer | Primary question(s) | |---|---| | Choose-tenant page | `selector_eligibility` | | Select-tenant controller | `selector_eligibility`, `remembered_context_validity` | | WorkspaceContext | `remembered_context_validity` | | OperateHubShell | `tenant_bound_viewability`, `canonical_linked_record_viewability`, `administrative_discoverability` | | TenantActionPolicySurface | `archive_eligibility`, `restore_eligibility`, `resume_onboarding_eligibility`, `verification_readiness_eligibility` | | ManagedTenantOnboardingWizard | `resume_onboarding_eligibility`, `onboarding_completion_eligibility`, `verification_readiness_eligibility` | | TenantResource global search and admin listing | `administrative_discoverability` | | TenantlessOperationRunViewer | `canonical_linked_record_viewability` and follow-up action questions | ## Migration Notes - No persistence migration is required for the first slice. - Existing boolean accessors on `TenantOperabilityService` may remain as compatibility adapters while consumers migrate to structured outcomes. - Any new enums or value objects should live under `app/Support/Tenants` to stay aligned with existing lifecycle, page-category, and action-surface support code.