TenantAtlas/specs/148-central-tenant-operability-policy/data-model.md
ahmido 417df4f9aa feat: central tenant operability policy (#177)
## Summary
- centralize tenant operability into a lane-aware, actor-aware policy boundary
- align selector eligibility, administrative discoverability, remembered context, tenant-bound routes, and canonical run viewers
- add focused Pest coverage plus Spec 148 artifacts and final polish task completion

## Validation
- `vendor/bin/sail artisan test --compact tests/Unit/Tenants/TenantOperabilityServiceTest.php tests/Unit/Tenants/TenantOperabilityOutcomeTest.php tests/Feature/Workspaces/ChooseTenantPageTest.php tests/Feature/Workspaces/SelectTenantControllerTest.php tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/OpsUx/OperateHubShellTest.php tests/Feature/Rbac/TenantLifecycleActionVisibilityTest.php tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php tests/Feature/Rbac/TenantResourceAuthorizationTest.php tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php tests/Feature/Filament/TenantGlobalSearchLifecycleScopeTest.php tests/Feature/Onboarding/OnboardingDraftLifecycleTest.php tests/Feature/Onboarding/OnboardingDraftAuthorizationTest.php`
- `vendor/bin/sail bin pint --dirty --format agent`
- manual browser smoke checks on `/admin/choose-tenant`, `/admin/tenants`, `/admin/onboarding`, `/admin/onboarding/{draft}`, and `/admin/operations/{run}`

## Filament / platform notes
- Livewire v4 compliance preserved
- panel provider registration unchanged in `bootstrap/providers.php`
- Tenant resource global search remains backed by existing view/edit pages and is now separated from active-only selector eligibility
- destructive actions remain action closures with confirmation and authorization enforcement
- no asset pipeline changes and no new `filament:assets` deployment requirement

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #177
2026-03-17 11:48:55 +00:00

6.6 KiB

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:

  • draftonboarding
  • onboardingactive
  • activearchived
  • archivedactive

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.