# Implementation Plan: Central Tenant Status Presentation **Branch**: `146-central-tenant-status-presentation` | **Date**: 2026-03-16 | **Spec**: [specs/146-central-tenant-status-presentation/spec.md](./spec.md) **Input**: Feature specification from `/specs/146-central-tenant-status-presentation/spec.md` **Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts. ## Summary Standardize tenant lifecycle rendering behind one authoritative presentation contract that extends the existing `TenantLifecycle` + `BadgeCatalog` architecture with explicit lifecycle semantics, helper copy, and invalid-data handling. Roll the shared contract through tenant list/detail summary surfaces, archived-tenant banner copy, choose-tenant and onboarding-linked surfaces, and the tenantless operations viewer plus its enterprise-detail summary payload so `draft`, `onboarding`, `active`, and `archived` always render intentionally, never as Unknown, and never get conflated with provider, verification, RBAC, or run status domains. ## Technical Context **Language/Version**: PHP 8.4.15 **Primary Dependencies**: Laravel 12, Filament 5, Livewire 4, Tailwind CSS 4 **Storage**: PostgreSQL (existing tenant and operation records only; no schema changes planned) **Testing**: Pest 4 feature and unit tests, including Filament and Livewire coverage **Target Platform**: Laravel Sail web application on Filament admin and workspace-canonical UI surfaces **Project Type**: Web application monolith **Performance Goals**: No render-time external calls, no material query-count regression on list/detail/viewer surfaces, and no extra polling or background work introduced **Constraints**: Preserve Spec 143 lifecycle semantics and Spec 145 action semantics; do not change authorization, lifecycle transitions, selector inclusion rules, or tenant/workspace ownership; centralize badge semantics under BADGE-001; invalid fallback must be reserved for truly non-canonical data only **Scale/Scope**: Workspace-owned tenant lifecycle presentation across tenant management, choose-tenant, onboarding-linked tenant references, and the tenantless operations viewer plus its `OperationRunResource` enterprise-detail summary payload, plus regression coverage for the primary surfaces ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - Inventory-first: PASS. This is presentation-only work over existing tenant and canonical records. No snapshot/backfill/write behavior changes. - Read/write separation: PASS. No new writes, previews, confirmations, queues, or audits are introduced. - Graph contract path: PASS. No Microsoft Graph calls are added or changed. - Deterministic capabilities: PASS. No capability derivation changes. - RBAC-UX: PASS. Authorization planes, 404/403 semantics, membership checks, and capability enforcement remain unchanged. Canonical viewers continue to rely on existing record-level authorization before any tenant lifecycle is shown. - Workspace isolation: PASS. No workspace-scoping changes. - RBAC-UX destructive confirmation: PASS. No new destructive actions. - RBAC-UX global search: PASS. This plan may touch global lifecycle presentation only if an already-authorized tenant result is rendered; it does not widen search behavior. - Tenant isolation: PASS. No cross-tenant access changes. - Run observability: PASS. No `OperationRun` creation, mutation, or monitoring behavior changes. - Ops-UX 3-surface feedback: PASS. Not applicable because no `OperationRun` workflow changes are introduced. - Ops-UX lifecycle/service ownership: PASS. No `OperationRun` transition changes. - Ops-UX summary counts/guards/system runs: PASS. Not applicable. - Automation/data minimization: PASS. No new jobs, locks, or logging paths. - Badge semantics (BADGE-001): PASS WITH REQUIRED CENTRALIZATION. The implementation must extend the existing `BadgeCatalog` / `BadgeRenderer` path instead of adding local lifecycle maps. - UI naming (UI-NAMING-001): PASS. Operator vocabulary remains `Draft`, `Onboarding`, `Active`, and `Archived`. - Filament UI Action Surface Contract: PASS. Presentation-only changes on existing surfaces; no action-surface exemption needed. - Filament UI UX-001 (Layout & IA): PASS. Existing layouts remain intact; lifecycle rendering changes must fit current table, infolist, and viewer structures. **Post-Design Re-check**: PASS. Phase 1 design keeps lifecycle presentation read-only, tenant-safe, badge-centralized, and action-surface neutral. ## Project Structure ### Documentation (this feature) ```text specs/146-central-tenant-status-presentation/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── tenant-lifecycle-presentation.openapi.yaml └── tasks.md ``` ### Source Code (repository root) ```text app/ ├── Filament/ │ ├── Pages/ │ │ ├── Operations/ │ │ └── Workspaces/ │ ├── Resources/ │ │ └── TenantResource.php │ └── System/Pages/Directory/ ├── Models/ │ └── Tenant.php ├── Services/ │ ├── Onboarding/ │ └── Tenants/ ├── Support/ │ ├── Badges/ │ └── Tenants/ resources/ └── views/ └── filament/ ├── pages/ └── widgets/ tests/ ├── Feature/ │ ├── Filament/ │ ├── Onboarding/ │ └── Rbac/ └── Unit/ ``` **Structure Decision**: Use the existing Laravel monolith structure and extend the current support-layer centralization points first: `app/Support/Tenants` for lifecycle semantics, `app/Support/Badges` for badge-level rendering, `app/Filament/Resources/TenantResource.php` and related pages for tenant surfaces, `app/Filament/Pages/Operations` for canonical viewer alignment, and focused Pest coverage in `tests/Feature` and `tests/Unit`. ## Phase 0 Research Summary - Confirmed the canonical lifecycle already exists in `App\Support\Tenants\TenantLifecycle` with the exact four required values and labels. - Confirmed badge semantics are already centralized through `BadgeCatalog`, `BadgeRenderer`, and `TenantStatusBadge`, but today that path only standardizes badge label/color/icon, not helper copy or richer cross-surface presentation density. - Confirmed tenant list/detail and system directory tenant tables already use `BadgeDomain::TenantStatus`, while choose-tenant currently communicates lifecycle indirectly in prose, archived tenant context is surfaced through a dedicated banner view, and operation viewers derive lifecycle context through `TenantOperabilityService` plus `OperationRunResource` enterprise-detail payload data rather than a dedicated lifecycle presentation contract. - Confirmed Filament v5 supports badge presentation through shared text/badge semantics, which aligns with continuing to feed UI badges from one renderer rather than introducing per-surface mappings. ## Phase 1 Design ### Implementation Approach 1. Introduce a dedicated tenant lifecycle presentation contract that sits on top of `TenantLifecycle` and returns: - canonical lifecycle value - canonical label - badge tone/icon data - concise helper text - detailed helper text - explicit invalid-data marker for non-canonical values only 2. Keep `BadgeCatalog` / `BadgeRenderer` as the badge primitive for BADGE-001 compliance, but stop treating badge spec alone as the full lifecycle presentation contract. 3. Route all in-scope lifecycle rendering through the same presentation source, with per-surface density adapters for: - table badge only - infolist badge plus helper text - tenant summary and archived-banner helper copy - selector/supporting prose - canonical viewer referenced-tenant banner or note 4. Audit existing lifecycle render points and tenant-adjacent statuses before rollout so lifecycle remains clearly separate from provider app status, RBAC, verification, and run state and no valid-state local mapping survives outside the shared contract. 5. Add regression tests that cover all four canonical lifecycle states plus invalid-data fallback behavior, including archived-banner and canonical-view summary surfaces. ### Planned Workstreams - **Workstream A: Central contract** Create a single presentation object or presenter in the tenant/support layer that derives from `TenantLifecycle` and is reusable outside tables. - **Workstream B: Tenant management surfaces** Update tenant list/detail summary areas, archived-tenant banner content, and any onboarding-linked tenant cards/sections to use the richer contract while preserving current Filament layouts and actions. - **Workstream C: Selector and operations viewer surfaces** Update choose-tenant and tenantless operation viewer context copy, including referenced-tenant banners and the `OperationRunResource` enterprise-detail summary payload consumed by that viewer, to use canonical lifecycle wording when lifecycle is shown or described. - **Workstream D: Regression hardening** Add focused unit tests for exhaustive lifecycle mapping and feature tests for tenant index/detail summary areas, archived banner content, onboarding-linked surfaces, and operation viewers. ## Testing Strategy - Add a focused unit test for the central lifecycle presentation contract covering `draft`, `onboarding`, `active`, `archived`, and explicit invalid fallback for non-canonical values. - Add a focused audit pass over the existing tenant lifecycle render points before rollout so every valid-state surface is routed through the shared contract. - Update or add tenant Filament feature tests to assert lifecycle labels on: - tenant index table - tenant detail identity and summary sections - archived tenant banner copy - choose-tenant empty or contextual copy where lifecycle is described - Update or add canonical viewer tests to assert onboarding and archived referenced tenants render intentional lifecycle context in both the tenantless viewer banner and the `OperationRunResource`-backed summary payload rendering. - Add at least one mixed-status assertion showing lifecycle remains distinct from provider or RBAC status on tenant detail. - Run the minimum focused Pest suite through Sail for the touched feature and unit tests. ## Complexity Tracking No constitution violations or exceptional complexity are planned at this stage.