TenantAtlas/specs/145-tenant-action-taxonomy-lifecycle-safe-visibility/research.md

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 TenantOperabilityDecision with 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 TenantResource for 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.