# Research: Central Tenant Status Presentation

## Decision 1: Build on the existing TenantLifecycle enum and badge catalog instead of creating a parallel status system

- Decision: Treat `App\Support\Tenants\TenantLifecycle` as the canonical lifecycle source and extend presentation through a new lifecycle presentation contract layered on top of the existing `BadgeCatalog` and `BadgeRenderer` pipeline.
- Rationale: The codebase already has the correct domain enum (`draft`, `onboarding`, `active`, `archived`) and an existing BADGE-001-compliant badge registry. Reusing those pieces avoids semantic drift and keeps lifecycle presentation aligned with the authoritative domain model from Spec 143.
- Alternatives considered:
  - Add more per-surface `match` or `formatStateUsing()` closures. Rejected because that would reintroduce scattered UI dictionaries.
  - Put all presentation logic directly on the `Tenant` model. Rejected because lifecycle presentation density varies by surface and should not overload the domain model with Filament-specific UI concerns.
  - Keep using `TenantStatusBadge` alone. Rejected because badge label/color/icon is not enough to cover helper text, referenced-tenant notes, and invalid-data distinction.

## Decision 2: Separate lifecycle presentation from adjacent status domains explicitly

- Decision: Keep lifecycle presentation as its own contract and do not merge it with provider connection, app consent, RBAC, verification, onboarding progress, or operation-run state.
- Rationale: The tenant detail and tenantless operations viewer surfaces already carry multiple status domains. Spec 146 requires lifecycle to remain honest to lifecycle only, otherwise operators misread onboarding, archived, or active state as health or readiness.
- Alternatives considered:
  - Collapse lifecycle and readiness into one composite badge. Rejected because it obscures whether a tenant is active but unhealthy versus onboarding but valid.
  - Reuse app-status or RBAC helper copy to explain lifecycle. Rejected because it creates cross-domain semantic leakage.

## Decision 3: Use one presentation source with multiple density variants

- Decision: The new presentation contract should return both concise and detailed variants from the same underlying lifecycle meaning.
- Rationale: Tables and selectors need short badge semantics, while detail views and the tenantless operations viewer need helper text that explains why onboarding or archived tenants still appear. One source with multiple densities satisfies the spec without forcing verbose copy into every surface.
- Alternatives considered:
  - Make all surfaces badge-only. Rejected because the tenantless operations viewer and detail pages need more context.
  - Make all surfaces badge plus paragraph text. Rejected because selector and list surfaces would become noisy.

## Decision 4: Keep invalid fallback only for non-canonical data and make it explicit in tests

- Decision: Preserve a reserved fallback path for corrupted or unexpected lifecycle values, but do not allow canonical states to reach it.
- Rationale: `TenantStatusBadge` currently falls back to `BadgeSpec::unknown()`. The feature needs a stricter separation so valid canonical states are exhaustive and only truly invalid values use fallback behavior.
- Alternatives considered:
  - Remove fallback completely. Rejected because legacy or bad data still needs a safe render path.
  - Continue allowing normalized aliases like `inactive` to map silently without explicit tests. Rejected because future drift would be hard to detect.

## Decision 5: Favor focused regression tests over broad UI rewrites

- Decision: Add unit coverage for the central presentation contract and focused Pest feature tests for the tenant index, tenant detail, choose-tenant/onboarding-linked presentation, and tenantless operations viewer surfaces.
- Rationale: The existing architecture is already partially centralized. The highest-value risk is regression through future local mappings or fallback behavior, so tests should lock down lifecycle semantics where operators actually see them.
- Alternatives considered:
  - Add only unit tests. Rejected because cross-surface consistency is the actual acceptance criterion.
  - Add a full browser test suite for every surface. Rejected because feature and unit coverage can prove the contract with lower cost and faster CI.

## Supporting Findings

- `App\Support\Tenants\TenantLifecycle` already defines the exact four lifecycle states and canonical labels required by the spec.
- `App\Support\Badges\BadgeCatalog` and `App\Support\Badges\Domains\TenantStatusBadge` already centralize badge label/color/icon mapping for tenant lifecycle, but they do not yet provide richer helper semantics.
- `app/Filament/Resources/TenantResource.php` already renders tenant `status` through `BadgeRenderer` on both the table and infolist surfaces.
- `resources/views/filament/pages/choose-tenant.blade.php` uses lifecycle-adjacent prose such as "No active tenants available" and references onboarding/archived records without a dedicated lifecycle presentation object.
- `resources/views/filament/widgets/tenant/tenant-archived-banner.blade.php` already carries archived-tenant helper copy, which makes it an in-scope surface that must be aligned with the shared lifecycle contract instead of maintaining standalone wording.
- `app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` derives lifecycle-aware canonical-view copy via `TenantOperabilityService`, which is a good integration point for the shared presentation contract.
- `resources/views/filament/pages/operations/tenantless-operation-run-viewer.blade.php` renders the canonical context banner body, making it the concrete viewer surface where referenced-tenant lifecycle wording must stay aligned with tenant management pages.
- `app/Filament/Resources/OperationRunResource.php` builds the enterprise-detail payload consumed by the tenantless operations viewer, so referenced-tenant lifecycle summary text there is part of the same viewer surface rather than a separate resource rollout.
- Filament v5 documentation confirms badge rendering is intended to be driven centrally through shared text/badge semantics rather than repeated local mappings.
