TenantAtlas/specs/147-tenant-selector-remembered-context-enforcement/data-model.md
ahmido 73a879d061 feat: implement spec 147 tenant context enforcement (#176)
## Summary
- implement Spec 147 for workspace-first tenant selector and remembered tenant context enforcement
- harden canonical and tenant-bound route behavior so selected tenant mismatch stays informational
- fix drift finding subject fallback for workspace-safe RBAC identifiers and centralize finding subject resolution

## Testing
- vendor/bin/sail artisan test --compact tests/Feature/Filament/FindingViewRbacEvidenceTest.php tests/Feature/Findings/FindingsListDefaultsTest.php
- vendor/bin/sail bin pint --dirty --format agent

## Notes
- branch pushed at de0679cd8b
- includes the spec artifacts under specs/147-tenant-selector-remembered-context-enforcement/

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #176
2026-03-16 22:52:58 +00:00

161 lines
5.4 KiB
Markdown

# Data Model: Tenant Selector and Remembered Context Enforcement
## Overview
This feature does not introduce a new persistence model. It formalizes how existing workspace, tenant, route, and remembered-context concepts interact at the shell and routing layers.
## Entities
### 1. Workspace Context
**Represents**: The primary operating boundary for the current admin session.
**Key fields**:
- `workspace_id`
- `workspace.slug`
- `workspace.archived_at`
**Relationships**:
- Owns many tenants
- Owns many canonical workspace records such as operation runs
- Owns one session-scoped remembered tenant preference map entry per user session
**Validation rules**:
- Must exist
- Must not be archived for active selection
- Actor must be a workspace member or the request resolves as deny-as-not-found
### 2. Tenant
**Represents**: A workspace-owned tenant record that may be active, onboarding, draft, or archived.
**Key fields**:
- `id`
- `workspace_id`
- `external_id`
- `status`
- `deleted_at`
- operator-facing identity fields such as `name`, `domain`, `environment`
**Relationships**:
- Belongs to one workspace
- May be a route subject on tenant-bound pages
- May be referenced by canonical workspace records such as operation runs
- May be persisted as remembered active-lane context only when selector-eligible
**Validation rules**:
- For standard active selector use, tenant must belong to current workspace, remain entitled to the actor, exist, and satisfy active-lane eligibility
- For tenant-bound route validity, tenant must satisfy workspace and entitlement checks; selector eligibility is not required
### 3. Remembered Tenant Context
**Represents**: A workspace-scoped user/session preference for the last active-lane tenant.
**Key fields**:
- `workspace_last_tenant_ids[workspace_id]` session entry
- optional persisted recency signals through `users.last_tenant_id` or `user_tenant_preferences.last_used_at`
**Relationships**:
- Belongs logically to one workspace context
- References one tenant candidate for active-lane convenience
**Validation rules**:
- Must only be read inside an established workspace context
- Must resolve to an existing tenant in the current workspace
- Must satisfy entitlement-sensitive access checks
- Must satisfy active-lane eligibility checks
- Invalid values must be cleared or ignored deterministically
### 4. Active Selector Option
**Represents**: A tenant that is eligible to appear in the standard active tenant selector.
**Derived from**:
- `TenantOperabilityDecision.canSelectAsContext`
**Required attributes**:
- tenant identity
- lifecycle presentation
- selector-safe label and helper copy
**Validation rules**:
- Must not include `draft`, `onboarding`, or `archived` tenants under the current lifecycle model
- Must not include tenants outside the current workspace
- Must not include tenants the actor cannot access
### 5. Route Subject
**Represents**: The record made authoritative by the current route.
**Variants**:
- tenant-bound route subject: `Tenant`
- canonical workspace record subject: `OperationRun` or other workspace-owned records
**Validation rules**:
- Route subject legitimacy is resolved from route record identity plus policy checks
- Remembered tenant context may influence convenience UI only and must not replace route authority
## Derived Domain Objects
### Tenant Operability Decision
**Represents**: The existing lifecycle-aware rule set produced by `TenantOperabilityService`.
**Relevant flags for this feature**:
- `canSelectAsContext`
- `canViewTenantSurface`
- `canReferenceInWorkspaceMonitoring`
### Shell Context Resolution Result
**Represents**: The runtime decision for what tenant, if any, the shell should treat as active convenience context.
**Possible states**:
- `route_authoritative_tenant`
- `validated_selected_tenant`
- `no_selected_tenant`
- `stale_context_cleared`
## State Transitions
### Remembered Tenant Context Lifecycle
1. `unset`
- No remembered tenant exists for the current workspace.
2. `remembered_active`
- An active-lane-eligible tenant is selected and stored for the current workspace.
3. `revalidated_active`
- A later request reads the remembered tenant and confirms workspace match, entitlement, existence, and selector eligibility.
4. `invalidated_cleared`
- A later request detects stale or ineligible remembered tenant context and clears or ignores it.
5. `no_selected_tenant`
- The shell falls back to a legitimate workspace-level no-tenant state.
**Invalidation triggers**:
- workspace switch
- tenant no longer exists
- tenant no longer belongs to current workspace
- tenant no longer satisfies active-lane eligibility
- actor no longer has tenant entitlement where required
### Page Semantics By Category
#### Workspace-level page
- Accepts `remembered_active`, `invalidated_cleared`, or `no_selected_tenant`
- Selected tenant may become a filter only
#### Tenant-bound page
- Route tenant is authoritative
- Selected tenant may match, differ, or be absent
#### Canonical workspace record viewer
- Route record is authoritative
- Referenced tenant may differ from selected tenant without invalidating the page
## Invariants
- Workspace is always the primary context boundary.
- Remembered tenant context never broadens authorization.
- Standard selector membership never implies universal tenant discoverability.
- Route legitimacy always outranks selected tenant context.
- No-selected-tenant is a valid workspace shell state.