## Summary
Implements Spec 145 for tenant action taxonomy and lifecycle-safe visibility.
This PR:
- adds a central tenant action policy surface and supporting value objects
- aligns tenant list, detail, edit, onboarding, and widget surfaces around lifecycle-safe actions
- standardizes operator-facing lifecycle wording around View, Resume onboarding, Archive, Restore, and Complete onboarding
- tightens onboarding and tenant lifecycle authorization semantics, including honest 404 vs 403 behavior
- updates related regression coverage and spec artifacts for Spec 145
- fixes follow-on full-suite regressions uncovered during validation, including onboarding browser flows, provider consent fixtures, workspace redirect DI expectations, and critical table/action/UI expectation drift
## Validation
Executed and passed:
- vendor/bin/sail bin pint --dirty --format agent
- vendor/bin/sail artisan test --compact
Result:
- 2581 passed
- 8 skipped
- 13534 assertions
## Notes
- Base branch: dev
- Feature branch commit: a33a41b
- Filament v5 / Livewire v4 compliance preserved
- No panel provider registration changes; Laravel 12 provider registration remains in bootstrap/providers.php
- No new globally searchable resource behavior added in this slice
- Destructive lifecycle actions remain confirmation-gated and authorization-protected
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #174
5.7 KiB
Phase 0 Research: Tenant Action Taxonomy and Lifecycle-Safe Visibility
Decision: Add a dedicated tenant-action policy surface on top of existing lifecycle services
Rationale: TenantLifecycle and TenantOperabilityService already provide canonical lifecycle and coarse operability booleans, but they do not model surface-specific action families, labels, confirmation semantics, or workflow-specific distinctions. TenantResource, ViewTenant, and EditTenant currently duplicate lifecycle action assembly. A dedicated tenant-action policy or resolver can consume the existing lifecycle decision and produce surface-aware action descriptors without moving workflow or persistence logic into the UI layer.
Alternatives considered:
- Extend
TenantOperabilityDecisionwith many more booleans: rejected because it would mix domain lifecycle with page-specific action inventory and quickly become a rigid flag bag. - Keep per-surface closures and only rename labels: rejected because the drift problem is already duplication, not just wording.
Decision: Keep onboarding completion inside onboarding workflow services and pages
Rationale: ManagedTenantOnboardingWizard and OnboardingLifecycleService already model resumability, lifecycle checkpoints, blocking reasons, and activation readiness. Treating activation as just another tenant table action would collapse workflow state into generic lifecycle mutation and violate the spec's “Restore is not Activation” rule.
Alternatives considered:
- Expose activation directly from
TenantResourcefor onboarding tenants: rejected because onboarding readiness is workflow-dependent and not a generic tenant-management mutation. - Reframe restore as activation when a tenant was previously onboarding: rejected because archived recovery and onboarding completion are different audit and operator intents.
Decision: Reuse existing audit action IDs and expand taxonomy through policy, not new lifecycle event names
Rationale: The repo already has AuditActionId::TenantArchived, TenantRestored, TenantReturnedToDraft, and onboarding-specific lifecycle audit IDs. The immediate need is semantic consistency between labels, visibility, and these existing audit IDs. That can be achieved without introducing new audit enum values in this spec slice.
Alternatives considered:
- Introduce a parallel action-event enum for UI taxonomy: rejected because it would split action naming from the canonical audit vocabulary without immediate benefit.
- Leave audit naming for a later hardening pass and ignore it in the plan: rejected because the spec explicitly requires audit-friendly semantics now.
Decision: Preserve centralized badge rendering through BadgeCatalog and TenantStatusBadge
Rationale: Tenant lifecycle badge rendering is already centralized and used by TenantResource and other admin surfaces. Spec 145 changes action semantics more than badge semantics, so the correct plan is to reuse BadgeCatalog and avoid page-local lifecycle label mappings.
Alternatives considered:
- Add local status-to-action wording helpers in resource classes: rejected because it would drift from BADGE-001 and duplicate lifecycle knowledge.
- Defer all tenant-status rendering considerations to Spec 146 and ignore badges entirely: rejected because touched surfaces still need centralized status semantics during this rollout.
Decision: Use existing RBAC enforcement helpers as the execution gate, with the new action policy driving visibility and grouping
Rationale: UiEnforcement and WorkspaceUiEnforcement already enforce the member-visible-disabled versus non-member-hidden patterns and canonical capability-registry usage. The missing layer is semantic validity before those enforcement helpers are applied. The action policy should therefore decide whether an action is lifecycle-valid for a surface; UiEnforcement should continue to decide capability-based disabled state and execution guards.
Alternatives considered:
- Move all lifecycle and RBAC logic into
UiEnforcement: rejected because lifecycle correctness is not purely an RBAC concern. - Let the policy layer also execute authorization side effects: rejected because policies and enforcement helpers already own server-side enforcement.
Decision: Extend existing Pest suites rather than creating a new “145” test namespace
Rationale: The workspace already has targeted suites for tenant resource authorization, edit-tenant archive enforcement, onboarding draft lifecycle, tenant switcher scope, archived tenant route access, and tenant operability decisions. Extending those suites keeps regression coverage close to the existing behavior seams and makes future failures easier to localize.
Alternatives considered:
- Create a single new spec-numbered feature test file: rejected because the behavior spans unit, Livewire, routing, and onboarding concerns already covered by established suites.
- Rely on manual verification because the change is “mostly labels”: rejected because the spec is explicitly semantic hardening, not a cosmetic rename.
Decision: No new database schema or provider contract is required
Rationale: The necessary domain inputs already exist on Tenant, TenantOnboardingSession, audit enums, badge mappers, and operability services. This feature should add derived policy and surface logic only.
Alternatives considered:
- Add a persistent tenant-action-state table: rejected because action availability is derived from lifecycle, workflow state, entitlement, and page context.
- Add Graph/provider metadata to decide archive or restore availability: rejected because these lifecycle actions are local administrative semantics, not provider operations.