## Summary - implement Spec 143 tenant lifecycle, operability, and tenant-context semantics across chooser, tenant management, onboarding, and canonical operation viewers - add centralized tenant lifecycle and operability support types, audit action coverage, and lifecycle-aware badge and action handling - add feature and unit coverage for tenant chooser eligibility, global search scoping, canonical operation access, onboarding authorization, and lifecycle presentation ## Testing - vendor/bin/sail artisan test --compact - vendor/bin/sail bin pint --dirty --format agent Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #172
65 lines
6.4 KiB
Markdown
65 lines
6.4 KiB
Markdown
# 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. |