TenantAtlas/specs/146-central-tenant-status-presentation/data-model.md
ahmido 6ca496233b feat: centralize tenant lifecycle presentation (#175)
## Summary
- add a shared tenant lifecycle presentation contract and referenced-tenant adapter for canonical lifecycle labels and helper copy
- align tenant, chooser, onboarding, archived-banner, and tenantless operation viewer surfaces with the shared lifecycle vocabulary
- add Spec 146 design artifacts, audit notes, and regression coverage for lifecycle presentation across Filament and onboarding surfaces

## Validation
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Feature/Badges/TenantStatusBadgeTest.php tests/Unit/Badges/TenantBadgesTest.php tests/Unit/Tenants/TenantLifecycleTest.php tests/Unit/Support/Tenants/TenantLifecyclePresentationTest.php tests/Feature/Filament/TenantLifecyclePresentationAcrossTenantSurfacesTest.php tests/Feature/Filament/ReferencedTenantLifecyclePresentationTest.php tests/Feature/Filament/TenantLifecycleStatusDomainSeparationTest.php tests/Feature/Filament/TenantViewHeaderUiEnforcementTest.php tests/Feature/Onboarding/TenantLifecyclePresentationCopyTest.php tests/Feature/Onboarding/OnboardingDraftAuthorizationTest.php tests/Feature/Onboarding/OnboardingDraftLifecycleTest.php`

## Notes
- Livewire v4.0+ compliance preserved; this change is presentation-only on existing Filament v5 surfaces.
- Panel provider registration remains unchanged in `bootstrap/providers.php`.
- No global-search behavior changed; no resource was newly made globally searchable or disabled.
- No destructive actions were added or changed.
- No asset registration strategy changed; existing deploy flow for `php artisan filament:assets` remains unchanged.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #175
2026-03-16 18:18:53 +00:00

4.3 KiB

Data Model: Central Tenant Status Presentation

1. TenantLifecycleState

  • Purpose: The canonical tenant lifecycle domain value already defined by Spec 143 and implemented in TenantLifecycle.
  • Source: Existing tenant domain model.
  • Fields:
    • value: enum string
      • Allowed values: draft, onboarding, active, archived
    • label: string
      • Allowed values: Draft, Onboarding, Active, Archived
  • Relationships:
    • Derived from one Tenant
    • Referenced by one TenantLifecyclePresentation
  • Validation rules:
    • Must be one of the four canonical lifecycle values for normal presentation flow
    • Non-canonical values must bypass canonical rendering and enter invalid-data fallback only
  • State transitions:
    • Not defined here; owned by Spec 143 and the tenant domain

2. TenantLifecyclePresentation

  • Purpose: The authoritative UI presentation contract for a lifecycle state.
  • Ownership: Presentation layer only; no persistence required.
  • Fields:
    • value: canonical lifecycle value
    • label: canonical operator-facing label
    • badge_color: concise badge/chip color token
    • badge_icon: icon token for badge or chip rendering
    • short_description: concise explanatory text for detail or selector surfaces
    • long_description: longer helper text for infolists, banners, or canonical viewers
    • is_invalid_fallback: boolean flag reserved for corrupted or unexpected input
  • Relationships:
    • Belongs to one TenantLifecycleState when canonical
    • May be constructed from a referenced tenant in a canonical viewer
  • Validation rules:
    • Canonical values must always produce is_invalid_fallback = false
    • Invalid fallback must never be used for draft, onboarding, active, or archived
    • label must match the canonical lifecycle vocabulary for canonical values

3. TenantLifecyclePresentationVariant

  • Purpose: Surface-specific density derived from the same underlying lifecycle meaning.
  • Ownership: Presentation composition only.
  • Fields:
    • surface: enum-like string
      • Expected values: table, detail, selector, canonical_viewer, banner
    • show_badge: boolean
    • show_short_description: boolean
    • show_long_description: boolean
    • show_context_note: boolean
  • Relationships:
    • Belongs to one TenantLifecyclePresentation
  • Validation rules:
    • Must not alter value or label
    • May change density only, never meaning

4. ReferencedTenantLifecycleContext

  • Purpose: Captures how lifecycle is shown when a tenant appears as context on another record, such as an operation run.
  • Ownership: Canonical viewer presentation only.
  • Fields:
    • tenant_id: integer or null
    • tenant_name: string or null
    • lifecycle_value: canonical lifecycle value or invalid raw value
    • viewer_context: string
      • Expected examples: operation_run, report, audit_reference
    • context_note: optional explanatory text describing why the referenced tenant may be non-active
  • Relationships:
    • References one Tenant
    • Uses one TenantLifecyclePresentation
  • Validation rules:
    • Must only be constructed after existing viewer authorization succeeds
    • Must not imply the canonical record is invalid when the tenant lifecycle is onboarding or archived

5. Existing Domain Models in Scope

Tenant

  • Existing model: App\Models\Tenant
  • Relevant existing fields:
    • status
    • deleted_at / soft-delete state
    • workspace_id
    • name
    • domain
    • app_status
    • rbac_status
  • Relevant derived methods:
    • lifecycle()
    • isDraft()
    • isOnboarding()
    • isArchived()

OperationRun

  • Existing model: App\Models\OperationRun
  • Relevant existing fields:
    • tenant_id
    • workspace_id
    • status
    • outcome
    • context
  • Role in this feature:
    • Supplies referenced tenant context only
    • No lifecycle ownership changes
    • No OperationRun state machine changes

6. Invariants

  • Tenant lifecycle presentation must remain exhaustive for canonical states.
  • Lifecycle presentation must not redefine lifecycle transitions or authorization.
  • Lifecycle and health-like domains must remain separate in the rendered UI.
  • Surface-specific rendering may change density, but not label or semantic meaning.
  • Invalid fallback is reserved exclusively for non-canonical lifecycle data.