TenantAtlas/specs/148-central-tenant-operability-policy/spec.md
ahmido 417df4f9aa feat: central tenant operability policy (#177)
## Summary
- centralize tenant operability into a lane-aware, actor-aware policy boundary
- align selector eligibility, administrative discoverability, remembered context, tenant-bound routes, and canonical run viewers
- add focused Pest coverage plus Spec 148 artifacts and final polish task completion

## Validation
- `vendor/bin/sail artisan test --compact tests/Unit/Tenants/TenantOperabilityServiceTest.php tests/Unit/Tenants/TenantOperabilityOutcomeTest.php tests/Feature/Workspaces/ChooseTenantPageTest.php tests/Feature/Workspaces/SelectTenantControllerTest.php tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/OpsUx/OperateHubShellTest.php tests/Feature/Rbac/TenantLifecycleActionVisibilityTest.php tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php tests/Feature/Rbac/TenantResourceAuthorizationTest.php tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php tests/Feature/Filament/TenantGlobalSearchLifecycleScopeTest.php tests/Feature/Onboarding/OnboardingDraftLifecycleTest.php tests/Feature/Onboarding/OnboardingDraftAuthorizationTest.php`
- `vendor/bin/sail bin pint --dirty --format agent`
- manual browser smoke checks on `/admin/choose-tenant`, `/admin/tenants`, `/admin/onboarding`, `/admin/onboarding/{draft}`, and `/admin/operations/{run}`

## Filament / platform notes
- Livewire v4 compliance preserved
- panel provider registration unchanged in `bootstrap/providers.php`
- Tenant resource global search remains backed by existing view/edit pages and is now separated from active-only selector eligibility
- destructive actions remain action closures with confirmation and authorization enforcement
- no asset pipeline changes and no new `filament:assets` deployment requirement

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #177
2026-03-17 11:48:55 +00:00

205 lines
26 KiB
Markdown

# Feature Specification: Central Tenant Operability Policy
**Feature Branch**: `148-central-tenant-operability-policy`
**Created**: 2026-03-14
**Status**: Draft
**Input**: User description: "Tenant lifecycle, action visibility, selector inclusion, tenant-bound route behavior, and canonical record viewing are currently governed by scattered local checks such as lifecycle string comparisons, soft-delete checks, ad hoc entitlement queries, and page-specific assumptions. The product needs one central operability policy layer that translates tenant lifecycle plus authorization context into explicit, reusable decisions."
## Spec Scope Fields *(mandatory)*
- **Scope**: workspace
- **Primary Routes**:
- `/admin/tenants`
- `/admin/tenants/{tenant}`
- `/admin/choose-tenant`
- `/admin/onboarding`
- `/admin/onboarding/{onboardingDraft}`
- `/admin/operations`
- `/admin/operations/{run}`
- Any in-scope surface that decides tenant visibility, action availability, context eligibility, or tenant-linked record access
- **Data Ownership**:
- Tenants remain workspace-owned records.
- Tenant lifecycle remains defined by Spec 143.
- Operation runs remain canonical workspace-level records with optional tenant references.
- Onboarding workflow records remain separate workspace-scoped workflow records that may link to tenants where applicable.
- This feature governs the decision layer that evaluates what is allowed, visible, selectable, viewable, or operable for a tenant in a given context.
- This feature does not change workspace or tenant ownership boundaries and does not introduce a second tenant model.
- **RBAC**:
- Authorization planes involved: admin `/admin` workspace-scoped routes, tenant-bound routes, onboarding workflow routes, and canonical workspace-level viewers that may reference a tenant.
- Non-members or actors lacking workspace scope or tenant entitlement for the record in question receive deny-as-not-found behavior.
- Members who can reach the surface but lack the capability for a specific tenant action or lifecycle mutation receive forbidden behavior.
- Selected tenant context is convenience state only and must never replace workspace membership, tenant entitlement, capability checks, or route identity.
- All in-scope authorization behavior must rely on the canonical capability registry and central server-side enforcement rather than raw role strings or local visibility-only checks.
For canonical-view specs, the spec MUST define:
- **Default filter behavior when tenant-context is active**: Workspace-level indexes such as `/admin/operations` may prefilter to the selected tenant as a convenience, but canonical viewer legitimacy and tenant-linked record validity must not depend on current selected tenant context.
- **Explicit entitlement checks preventing cross-tenant leakage**: Canonical viewers must authorize from the workspace-owned record, the record's workspace relationship, the actor's workspace membership, and tenant entitlement for any referenced tenant. The operability layer may influence tenant follow-up actions, but it must not leak unauthorized tenant details or reintroduce selected-tenant-equality gating.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Get One Authoritative Tenant Decision (Priority: P1)
As a workspace operator, I need the product to answer the same tenant-semantic question the same way across selectors, actions, tenant pages, and linked record viewers, so that lifecycle and access rules stop changing from surface to surface.
**Why this priority**: Cross-surface inconsistency is the root failure. Until one central authority exists, every downstream surface can drift and reintroduce contradictory tenant behavior.
**Independent Test**: Can be fully tested by creating tenants in `draft`, `onboarding`, `active`, and `archived` states and verifying that selector eligibility, archive or restore availability, onboarding resume availability, and tenant-bound page viewability all resolve from one shared decision source instead of page-local exceptions.
**Acceptance Scenarios**:
1. **Given** the same `active` tenant appears on the selector, tenant index, and tenant detail page, **When** each surface asks whether the tenant is eligible for normal active context or archive, **Then** each surface receives the same operability answer.
2. **Given** the same `onboarding` tenant appears on onboarding and tenant-management surfaces, **When** each surface asks whether onboarding may be resumed, **Then** the answer remains consistent across those surfaces.
3. **Given** the same `archived` tenant appears in an administrative list and as a reference on a canonical record, **When** each surface asks whether restore is valid and whether the tenant is selector-eligible, **Then** the policy returns distinct but consistent lane-aware answers.
---
### User Story 2 - Keep Valid Records Viewable Without False Tenant Context Gates (Priority: P2)
As a workspace operator, I need tenant-bound routes and canonical workspace-level viewers to stay valid based on route subject and entitlement rather than remembered tenant context, so that deep links, investigations, and administrative review remain trustworthy.
**Why this priority**: Canonical viewers and tenant-bound routes are high-trust surfaces. False invalidity caused by header state undermines the workspace-first model and blocks investigation.
**Independent Test**: Can be fully tested by opening tenant detail routes and canonical operation-run routes while selected tenant context is mismatched, invalid, or empty and confirming the route still resolves when entitlement allows it.
**Acceptance Scenarios**:
1. **Given** an authorized user opens `/admin/operations/{run}` for a run linked to tenant A while tenant B is selected, **When** the page loads, **Then** the run remains viewable and the tenant mismatch is treated as context only.
2. **Given** an authorized user opens `/admin/tenants/{tenant}` for an onboarding or archived tenant that is excluded from the standard selector, **When** the page loads, **Then** the tenant page remains valid because the route tenant and policy are authoritative.
3. **Given** a user lacks workspace membership or tenant entitlement for a route-resolved record, **When** the user opens the route directly, **Then** deny-as-not-found behavior applies and the operability layer does not broaden access.
---
### User Story 3 - Show Only Lifecycle-Safe Actions And Selections (Priority: P3)
As a workspace operator, I need selectors and action surfaces to offer only lifecycle-safe choices for the tenant's current lane, so that I do not see actions or context options the product will reject later.
**Why this priority**: Honest selectors and honest action visibility reduce operator confusion and are the most visible outcome of correct operability policy.
**Independent Test**: Can be fully tested by preparing tenants in each lifecycle state and verifying that standard selector membership, archive or restore availability, resume-onboarding availability, and readiness or verification affordances match policy expectations in each lane.
**Acceptance Scenarios**:
1. **Given** a tenant is `draft` or `onboarding`, **When** the operator opens the standard active selector, **Then** the tenant is not selectable there.
2. **Given** a tenant is `active`, **When** the operator views tenant-management surfaces, **Then** normal operating-lane affordances may be available and onboarding-only actions are not shown.
3. **Given** a tenant is `archived`, **When** the operator views administrative recovery surfaces, **Then** restore may be offered when authorized and normal active-lane affordances are not shown.
### Edge Cases
- A remembered tenant was valid when stored but later becomes `draft`, `onboarding`, or `archived`; the active-lane remembered context must be invalidated without breaking workspace-level pages.
- A canonical workspace-level record references an onboarding or archived tenant; the record remains viewable when authorization permits even though the tenant is not active-selector eligible.
- A tenant has linked onboarding workflow records but is already `active`; onboarding-only actions must stay unavailable outside explicit workflow context.
- A tenant transitions lifecycle while an operator is already on a tenant-bound page; the page remains route-valid, but action availability and lane affordances refresh to the new policy outcome.
- A tenant is administratively discoverable but the actor lacks the capability for archive, restore, or resume-onboarding; the tenant remains viewable where allowed while the action itself stays unavailable or forbidden if reached directly.
## Requirements *(mandatory)*
**Constitution alignment (required):** This feature introduces no new Microsoft Graph calls, new long-running jobs, or new `OperationRun` types. It defines a central domain-policy layer that future and current in-scope consumers must use when resolving tenant visibility, action validity, context eligibility, and tenant-linked route access. Any lifecycle mutation that consumes this policy remains subject to existing confirmation, audit, tenant-isolation, and observability rules defined elsewhere.
**Constitution alignment (OPS-UX):** This feature does not create or mutate an `OperationRun`. It preserves the existing three-surface Ops-UX contract and clarifies that canonical viewers remain record-authoritative. Any existing or future `OperationRun`-backed surface that consumes operability decisions must continue to rely on service-owned status and outcome transitions and must not use operability as a substitute for run lifecycle ownership.
**Constitution alignment (RBAC-UX):** This feature changes authorization behavior in the admin `/admin` plane and the tenant-context behavior inside that plane. No platform `/system` behavior is broadened. Cross-plane access remains deny-as-not-found when out of scope. In this feature, 404 means the actor is not a workspace member or is not entitled to the workspace or tenant scope of the requested record; 403 means the actor is in scope but lacks a required capability for an otherwise valid action or route. Authorization must be enforced server-side through central policies, Gates, or equivalent authoritative services using the canonical capability registry. Global search and record discovery must remain non-member-safe and tenant-safe. Destructive-like actions such as `Archive` and `Restore` remain confirmation-required. Validation must include positive and negative authorization coverage plus capability-denied versus lifecycle-denied distinctions.
**Constitution alignment (OPS-EX-AUTH-001):** Not applicable. This feature does not alter authentication handshake behavior or permit synchronous auth-path exceptions to substitute for monitoring or operations rules.
**Constitution alignment (BADGE-001):** This feature may influence whether lifecycle-aware reasons and statuses are shown on surfaces, but lifecycle badge and label semantics remain centralized through the shared tenant-status presentation contract defined by Spec 146.
**Constitution alignment (UI-NAMING-001):** The target objects are tenant records, linked onboarding workflow records, and canonical workspace records with tenant references. Primary operator verbs remain `Archive`, `Restore`, `Resume onboarding`, `View`, and lane-appropriate inspection verbs. Operability explanations must preserve the distinction between selected tenant, route tenant, referenced tenant, lifecycle state, and capability denial. Implementation-first phrases such as `soft delete` or `not trashed` must not become primary operator-facing language.
**Constitution alignment (Filament Action Surfaces):** This feature modifies the decision authority behind existing Filament and shell-adjacent surfaces. The Action Surface Contract is satisfied when affected screens consume the central operability answers, keep destructive-like lifecycle actions confirmation-protected, and stop relying on resource-local lifecycle predicates as the final authority. The matrix below documents the in-scope action surfaces.
**Constitution alignment (UX-001 — Layout & Information Architecture):** This feature changes policy semantics rather than page layout. Existing Create, Edit, View, and Table layouts remain intact. UX-001 remains satisfied so long as affected surfaces preserve their existing layout structure while consuming the centralized policy answers for visibility, actions, and mismatch messaging.
### Functional Requirements
- **FR-148-001**: The system MUST provide one explicit, authoritative tenant operability policy layer for in-scope tenant-semantic decisions.
- **FR-148-002**: The central operability policy MUST evaluate decisions using an explicit evaluation contract that combines consumer-supplied context and authoritative service-resolved facts. That contract MUST cover the tenant record, tenant lifecycle, workspace relationship, actor, requested interaction lane or route context, linked record context where applicable, onboarding workflow state where required, and any authoritative membership, entitlement, capability, or archived persistence facts needed for the decision.
- **FR-148-003**: The central operability policy MUST support lane-aware evaluation for at least the standard active operating lane, onboarding workflow lane, administrative tenant-management lane, and canonical workspace-level record lane.
- **FR-148-004**: The central operability policy MUST centrally answer whether a tenant is eligible for the standard active selector.
- **FR-148-005**: The central operability policy MUST centrally answer whether remembered tenant context remains valid for normal active-lane use.
- **FR-148-006**: The central operability policy MUST centrally answer whether a tenant-bound route or tenant-bound page is viewable for a given actor and lane.
- **FR-148-007**: The central operability policy MUST centrally answer whether a canonical workspace-level record linked to a tenant remains viewable and what tenant follow-up affordances remain valid.
- **FR-148-008**: The central operability policy MUST centrally answer whether lifecycle actions such as `Archive`, `Restore`, and `Resume onboarding` are available for the tenant in the current lane.
- **FR-148-009**: The central operability policy MUST centrally answer whether onboarding-completion and readiness or verification actions are meaningful for the tenant in the current workflow or management context.
- **FR-148-010**: The central operability policy MUST centrally answer whether a tenant remains administratively discoverable even when it is not eligible for standard active selection.
- **FR-148-011**: Operability decisions MUST incorporate both authorization context and lifecycle or context semantics. Neither lifecycle alone nor capability alone may serve as the complete decision rule.
- **FR-148-012**: New or changed in-scope code MUST NOT use raw lifecycle checks, raw soft-delete checks, selector query filters, or selected-tenant equality as the final authority for operability decisions when the central policy applies.
- **FR-148-013**: If two in-scope consumers ask the same semantic question, the system MUST return the same operability answer even if the surfaces present that answer differently.
- **FR-148-014**: The central operability policy MUST preserve the rule that selected tenant context is convenience state and must not be the final authority for route legitimacy or canonical linked-record access.
- **FR-148-015**: For `draft` tenants, the policy MUST support administrative viewability and onboarding-lane activity while denying standard active-selector eligibility, archive, and restore.
- **FR-148-016**: For `onboarding` tenants, the policy MUST support onboarding-lane activity, administrative viewability, and resumable onboarding where authorized while denying standard active-selector eligibility, archive, and restore.
- **FR-148-017**: For `active` tenants, the policy MUST support standard active-selector eligibility when entitled, normal operating-lane affordances, and archive availability where authorized while denying onboarding-only actions and restore.
- **FR-148-018**: For `archived` tenants, the policy MUST deny standard active-selector eligibility and normal operating-lane affordances while allowing administrative or audit-lane viewability and restore where authorized.
- **FR-148-019**: The central operability policy SHOULD provide structured outcomes that can express allowed or denied state plus reason information sufficient to distinguish capability denial, lifecycle mismatch, wrong lane, selector ineligibility, and invalid lifecycle-action combinations.
- **FR-148-020**: Structured operability outcomes MUST be usable by selectors, remembered-context validation, tenant table actions, tenant detail actions, tenant-bound access checks, onboarding workflow surfaces, canonical viewers, and future governance or reporting surfaces.
- **FR-148-021**: The central operability policy MUST support gradual adoption so existing consumers can migrate incrementally without introducing a parallel second semantics system.
- **FR-148-022**: In-scope implementations MUST reduce repeated tenant-semantic branching in pages, resources, helpers, middleware, and shell logic by moving equivalent decisions to the central policy boundary.
- **FR-148-023**: The operability layer MUST complement real authorization rather than weaken it; it must never broaden access to tenant or workspace records beyond existing membership, entitlement, and capability rules.
- **FR-148-024**: Validation coverage for this feature MUST include each canonical lifecycle state, selector eligibility, remembered-context validity, tenant-bound viewability, canonical linked-record compatibility, archive eligibility, restore eligibility, resume-onboarding eligibility, readiness-action eligibility where relevant, and capability-denied versus lifecycle-denied outcomes.
## UI Action Matrix *(mandatory when Filament is changed)*
If this feature adds/modifies any Filament Resource / RelationManager / Page, fill out the matrix below.
For each surface, list the exact action labels, whether they are destructive (confirmation? typed confirmation?),
RBAC gating (capability + enforcement helper), and whether the mutation writes an audit log.
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Workspace shell and standard tenant selector | `/admin`, `/admin/choose-tenant` | Existing workspace and tenant-context affordances only | Existing tenant chooser entries | `Select tenant` for standard active-lane eligible tenants only | None | Existing fallback CTA to managed tenants or workspace surfaces remains | Not applicable | Not applicable | No direct mutation | The operability policy becomes the authority for selector inclusion and remembered-context validity. |
| Managed tenants index | `/admin/tenants` | Existing create or onboarding-entry actions remain as currently defined | Existing row click or `View` affordance | `Resume onboarding`, `Archive`, or `Restore` depending on policy; additional actions remain overflowed | Existing grouped bulk actions unchanged unless later specs harden them | Existing tenant creation or onboarding CTA remains | Not applicable | Existing save and cancel behavior unchanged | Yes for lifecycle mutations | `Archive` and `Restore` remain destructive-like and require confirmation plus capability enforcement. |
| Tenant detail page | `/admin/tenants/{tenant}` | No new header actions are introduced by this spec | Route-record inspection | Not applicable | None | Not applicable | `Archive`, `Restore`, `Resume onboarding`, and related inspection actions only when the central policy says they are valid | Existing save and cancel behavior unchanged | Yes for lifecycle mutations | Route legitimacy is resolved from the route tenant and policy, not selected-tenant equality. |
| Onboarding workflow pages | `/admin/onboarding`, `/admin/onboarding/{onboardingDraft}` | Existing onboarding entry actions remain | Existing onboarding record inspection | `Resume onboarding` and related workflow actions when valid | None | Existing onboarding CTA remains | `View tenant` and workflow-appropriate actions when valid | Existing save and cancel behavior unchanged | Yes when workflow state mutates | Onboarding surfaces consume the same operability answers but remain workflow-lane pages, not active-lane selectors. |
| Operations index and canonical run viewer | `/admin/operations`, `/admin/operations/{run}` | Existing navigation and filtering affordances remain | Existing run inspection links remain | `View run` remains canonical inspect action | Existing grouped bulk actions unchanged | Existing empty state unchanged | Existing run-view actions unchanged | Not applicable | Existing audit rules unchanged | Exemption: this spec changes viewer legitimacy and tenant-linked affordance policy, not the run mutation inventory. |
### Key Entities *(include if feature involves data)*
- **Tenant Operability Policy**: The authoritative domain-policy layer that translates lifecycle, authorization context, and interaction lane into explicit tenant decisions.
- **Interaction Lane**: The product context in which a tenant is being evaluated, including active operating, onboarding workflow, administrative management, and canonical workspace-record viewing.
- **Tenant**: A durable workspace-owned record with canonical lifecycle semantics that influence, but do not fully determine, operability.
- **Onboarding Workflow Record**: A workspace-scoped workflow record linked to a tenant where applicable and used to evaluate onboarding-specific operability decisions.
- **Canonical Workspace Record**: A workspace-owned record such as an operation run that may reference a tenant while remaining authoritative on its own route.
- **Remembered Tenant Context**: A workspace-scoped convenience preference for the active operating lane that must be revalidated through the central operability policy before use.
- **Operability Outcome**: A centralized decision result that communicates whether a tenant is eligible, viewable, or actionable in a given lane and why.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-148-001**: In focused regression coverage for in-scope consumers, 100% of shared tenant-semantic decisions tested across at least two surfaces return consistent outcomes for the same tenant, actor, and lane.
- **SC-148-002**: In focused regression coverage, 0 `draft`, `onboarding`, or `archived` tenants appear as selectable choices in the standard active selector unless a future spec explicitly redefines selector policy.
- **SC-148-003**: In focused regression coverage, 100% of authorized canonical record links and tenant-bound routes remain accessible when selected tenant context is mismatched, stale, or empty.
- **SC-148-004**: In focused regression coverage, 100% of lifecycle-changing and onboarding-resume actions tested resolve from the central operability layer rather than page-local lifecycle shortcuts.
- **SC-148-005**: In focused authorization coverage, 100% of non-member or non-entitled access attempts resolve as deny-as-not-found and 100% of in-scope member-without-capability cases resolve as forbidden.
- **SC-148-006**: In focused validation of denied or ineligible states, every covered denial path communicates a structured lifecycle-, lane-, or capability-aware reason suitable for clearer UX and regression assertions.
## Assumptions
- Spec 143 remains the source of truth for tenant lifecycle semantics and page-type distinctions.
- Spec 144 remains the source of truth for canonical operation-viewer legitimacy under tenant-context mismatch.
- Spec 145 remains the source of truth for lifecycle-safe tenant action taxonomy.
- Spec 146 remains the source of truth for centralized tenant-status and lifecycle presentation.
- Spec 147 remains the source of truth for selector semantics and remembered tenant-context enforcement.
- This feature is a consolidation layer that centralizes semantics already implied by those specs rather than introducing a new tenant or authorization model.
## Dependencies
- Spec 143 — Tenant Lifecycle, Operability, and Context Semantics Foundation
- Spec 144 — Canonical Operation Viewer Decoupled from Remembered Tenant Context
- Spec 145 — Tenant Action Taxonomy and Lifecycle-Safe Visibility
- Spec 146 — Central Tenant Status Presentation
- Spec 147 — Tenant Selector and Remembered Context Enforcement
## Risks
- A boolean-only policy surface could centralize decisions without improving diagnostics, operator messaging, or test clarity.
- Partial consumer migration could leave some legacy local checks in place and delay full consistency.
- Overloading the operability layer with presentation or persistence concerns could turn it into a generic dumping ground rather than a clean semantic boundary.
- Treating operability as a replacement for authorization rather than as a complement could weaken the current security model.
## Summary
Tenant behavior is currently enforced through scattered lifecycle comparisons, soft-delete checks, selector filters, page-local visibility rules, and route-specific assumptions. This makes the same tenant behave differently depending on surface, hides policy bugs behind local shortcuts, and makes future lifecycle-aware work too easy to implement incorrectly.
This feature establishes one central tenant operability policy layer whose job is to answer the recurring product question: what may this actor do with this tenant in this lane, and why? That central policy becomes the bridge between lifecycle meaning, authorization context, selector behavior, tenant-bound routes, canonical linked-record viewers, and lifecycle-safe action availability.