# Research: Tenant Lifecycle, Operability, and Context Semantics Foundation

## Decision 1: Reuse the existing `Tenant` lifecycle states as the canonical domain source

- Decision: Keep `draft`, `onboarding`, `active`, and `archived` on `App\Models\Tenant` as the single canonical lifecycle vocabulary for tenant records, and build operability and context rules on top of that rather than introducing a second lifecycle store.
- Rationale: `Tenant` already defines the lifecycle constants, soft-delete behavior, and current-selection safety (`makeCurrent()` rejects non-active tenants). The inconsistency is not missing states; it is inconsistent interpretation of those states across pages and helpers.
- Alternatives considered:
  - Add a second lifecycle table or projection. Rejected because it would duplicate the same tenant-state truth and increase reconciliation complexity.
  - Derive tenant lifecycle entirely from onboarding session lifecycle. Rejected because onboarding drafts are workflow records and do not replace the durable tenant record.

## Decision 2: Model operability separately from lifecycle

- Decision: Introduce a central operability layer that answers viewability, selector eligibility, action eligibility, onboarding resumability, and canonical monitoring referenceability independently of raw lifecycle.
- Rationale: Current code uses raw `where('status', 'active')`, `isActive()`, or soft-delete checks in selectors, controllers, and actions. That pattern conflates lifecycle with every UX and authorization choice.
- Alternatives considered:
  - Continue using direct `status === 'active'` checks with better comments. Rejected because it does not prevent future divergence.
  - Encode every decision directly into policies. Rejected because selector visibility, badge presentation, and page categorization are not purely authorization concerns.

## Decision 3: Treat remembered tenant context as preference state, not route legitimacy

- Decision: Keep remembered tenant state in workspace context helpers, but treat it as filter and navigation preference only. Canonical workspace-owned record viewers must authorize by record and entitlement, not by active tenant equality.
- Rationale: `TenantlessOperationRunViewer::mount()` currently aborts 404 when the active tenant differs from the run tenant even after `OperationRunPolicy` already authorizes the user. That is the concrete trust failure this foundation must eliminate.
- Alternatives considered:
  - Keep the viewer mismatch 404 and document it. Rejected because it makes a valid record appear missing and violates the spec’s canonical-record rule.
  - Automatically switch the selected tenant when viewing a canonical record. Rejected because it mutates operator context as a side effect of inspection.

## Decision 4: Keep onboarding workflow state distinct from tenant lifecycle

- Decision: Preserve `TenantOnboardingSession` as the owner of workflow progression, resumability, and checkpoint semantics, while `Tenant` owns lifecycle semantics for the durable tenant record.
- Rationale: The codebase already uses `OnboardingLifecycleService`, `OnboardingDraftResolver`, and `TenantOnboardingSessionPolicy` to manage workflow-specific state. Folding that into tenant lifecycle would blur durable domain state with wizard progression.
- Alternatives considered:
  - Collapse onboarding lifecycle into tenant status. Rejected because checkpoint-level progress, resumability, and versioning belong to the workflow record.
  - Hide onboarding tenants until activation. Rejected because operators need to resume, inspect, and audit onboarding work before activation.

## Decision 5: Extend centralized badge semantics instead of page-local mappings

- Decision: Use `BadgeCatalog` and `BadgeDomain` as the only badge semantics extension point for tenant lifecycle presentation, and add exhaustive coverage tests there.
- Rationale: Tenant status in `TenantResource` already uses `BadgeRenderer::label/color/icon(BadgeDomain::TenantStatus)`. The fix for `Unknown` rendering belongs in the badge domain mapping, not in per-page string conditionals.
- Alternatives considered:
  - Patch each affected page with its own match statement. Rejected because it recreates drift.
  - Render raw status strings without badge mapping. Rejected because it breaks BADGE-001 and current UI consistency.

## Decision 6: Anchor authorization in existing policies and capability registries

- Decision: Follow-up implementation work should keep server-side enforcement in `OperationRunPolicy`, tenant policies, onboarding policies, `CapabilityResolver`, `WorkspaceCapabilityResolver`, and `Capabilities`, with any new lifecycle-aware decisions delegated through central helpers rather than raw capability strings.
- Rationale: The repo already has a strong split between 404 membership failures and 403 capability failures. The problem is contextual misuse, not absence of enforcement primitives.
- Alternatives considered:
  - Introduce route-local authorization closures. Rejected because it would increase duplication and drift.
  - Rely on Filament visibility or disabling state alone. Rejected because the constitution explicitly forbids using UI as the security boundary.

## Decision 7: Plan follow-up implementation around existing test clusters

- Decision: Target follow-up validation through existing Pest suites in `tests/Feature/Auth`, `tests/Feature/Monitoring`, `tests/Feature/Onboarding`, `tests/Feature/Filament`, `tests/Feature/Rbac`, `tests/Feature/TenantRBAC`, plus focused unit coverage for onboarding and badge semantics.
- Rationale: The repo already has dedicated tests for tenant chooser selection, tenant switcher scope, archived tenant access, onboarding authorization, canonical monitoring behavior, and operation-run viewing.
- Alternatives considered:
  - Create a new isolated test suite just for lifecycle semantics. Rejected because the behavior is cross-cutting and should strengthen the existing regression clusters.

## Filament and panel notes

- Filament v5 runs on Livewire v4 in this repository.
- Panel providers are registered in `bootstrap/providers.php`, which already contains `AdminPanelProvider`, `TenantPanelProvider`, and `SystemPanelProvider`.
- Relevant searchable-resource posture in current scope:
  - `TenantResource` has a resource view surface and can remain globally searchable if desired.
  - `OperationRunResource` has global search disabled.
  - Workspace landing and onboarding wizard are pages, not resources.