TenantAtlas/specs/280-workspace-tenancy-environment-routing/data-model.md
Ahmed Darrazi 40a33cdc15
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m35s
spec: prepare workspace tenancy environment routing cutover
2026-05-07 12:12:39 +02:00

192 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Model: Filament Workspace Tenancy & Environment Routing Cutover
**Date**: 2026-05-07
**Branch**: `280-workspace-tenancy-environment-routing`
## Overview
This slice introduces no new persistence. It replaces a temporary routing and panel-tenancy shell with a workspace-first runtime contract built on existing `Workspace` and `ManagedEnvironment` truth. The data model for this package is therefore a set of derived route, view-model, and navigation contracts that implementation must keep aligned across pages, middleware, and shared link builders.
## Persisted Truth Unchanged
- `Workspace` remains the workspace-owned root context.
- `ManagedEnvironment` remains the environment-scoped managed target inside one workspace.
- No table ownership, foreign-key ownership, artifact ownership, or role/capability family changes are introduced in this slice.
- Provider registration remains in `apps/platform/bootstrap/providers.php`; no new provider or asset persistence is introduced.
## Derived Runtime Contracts
### 1. Workspace Admin Context
**Persistence**: derived from route parameters plus `WorkspaceContext` session state
**Owner**: workspace-first admin shell
| Field | Type | Required | Notes |
|---|---|---|---|
| `workspace_id` | int | yes | Canonical workspace scope for the current request |
| `workspace_slug` | string | yes | Route-safe workspace identifier |
| `workspace_name` | string | yes | Operator-visible workspace label |
| `entry_mode` | string | yes | `chooser` or `dashboard` |
| `remembered_environment_id` | int | no | Derived only when the remembered environment belongs to the same workspace |
**Rules**:
- `/admin` resolves through this contract to either workspace selection or the canonical workspace dashboard.
- Non-member workspace access remains `404`.
- Switching workspaces invalidates any remembered environment that belongs to another workspace before any environment page renders.
### 2. Workspace Dashboard View
**Persistence**: none, derived from `WorkspaceOverviewBuilder`
**Owner**: `WorkspaceOverview`
| Field | Type | Required | Notes |
|---|---|---|---|
| `workspace` | object | yes | Workspace summary for the active workspace |
| `overview_payload` | array | yes | Existing builder output for signal cards, summaries, and quick actions |
| `environment_chooser_url` | string | yes | Canonical workspace-scoped environment chooser route |
| `operations_url` | string | yes | Canonical workspace operations route |
**Rules**:
- The workspace dashboard is the primary decision surface after workspace selection.
- It must not silently behave like a second environment dashboard.
- It reuses current builder output rather than introducing a new summary system.
### 3. Environment Chooser View
**Persistence**: none, derived from `ManagedTenantsLanding` data plus existing `ChooseTenant` selection logic
**Owner**: workspace-scoped environment chooser surface
| Field | Type | Required | Notes |
|---|---|---|---|
| `workspace` | object | yes | Active workspace summary |
| `environments` | list<object> | yes | Selectable managed environments within the workspace |
| `open_environment_url` | string | yes | Workspace-first environment dashboard URL per row/card |
| `switch_workspace_url` | string | no | Secondary escape hatch back to workspace choice |
**Environment item fields**:
| Field | Type | Required | Notes |
|---|---|---|---|
| `id` | int | yes | ManagedEnvironment key |
| `slug` | string | yes | Route-safe environment identifier |
| `name` | string | yes | Operator-visible environment label |
| `lifecycle_status` | string | yes | Existing operability/lifecycle status |
| `posture_hint` | string | no | Existing discoverability/operability summary only |
**Rules**:
- Only environments belonging to the active workspace and accessible to the actor appear.
- Archived or otherwise non-selectable environments do not appear and do not resolve.
- `ChooseTenant` may survive as an implementation seam, but not as a second public chooser contract.
### 4. Managed Environment Page Context
**Persistence**: derived from workspace-first route parameters plus `WorkspaceContext`
**Owner**: all environment-scoped pages touched by this slice
| Field | Type | Required | Notes |
|---|---|---|---|
| `workspace_id` | int | yes | Outer scope boundary |
| `workspace_slug` | string | yes | Outer scope route key |
| `managed_environment_id` | int | yes | Nested environment scope |
| `managed_environment_slug` | string | yes | Nested environment route key |
| `managed_environment_name` | string | yes | Operator-visible environment label |
| `breadcrumb_segments` | list<object> | yes | `Workspace -> Managed Environment -> page` |
| `page_category` | string | yes | Derived route classification after legacy families are removed |
**Rules**:
- The `{environment}` parameter must belong to the `{workspace}` parameter or the request is `404`.
- Breadcrumb and context-bar ordering must always be `Workspace -> Managed Environment -> page`.
- No touched page may keep `/admin/t` or `/admin/tenants/{environment}` as a valid public route.
### 5. Managed Environment Dashboard View
**Persistence**: none, derived from `TenantDashboardSummaryBuilder` and current dashboard widgets
**Owner**: `TenantDashboard`
| Field | Type | Required | Notes |
|---|---|---|---|
| `workspace` | object | yes | Outer workspace summary for breadcrumb/context |
| `managed_environment` | object | yes | Active environment summary |
| `dashboard_summary` | array | yes | Existing summary-builder payload |
| `primary_follow_up_url` | string | no | Existing recommended-action destination |
| `operations_url` | string | yes | Canonical workspace operations route with explicit environment filter |
**Rules**:
- The surface remains the canonical environment dashboard.
- Existing widget and header-action ownership stays intact.
- Operations navigation from this surface always enters the workspace operations family with explicit environment context.
### 6. Workspace Operations Scope
**Persistence**: derived route/query/session state already modeled by `Monitoring\Operations`
**Owner**: canonical workspace operations hub and detail viewer
| Field | Type | Required | Notes |
|---|---|---|---|
| `workspace_id` | int | yes | Required workspace scope |
| `managed_environment_id` | int | no | Optional environment prefilter |
| `tenant_scope` | string | no | Existing workspace-wide versus narrowed state flag |
| `active_tab` | string | no | Existing operations tab state |
| `problem_class` | string | no | Existing scoped deep-link filter |
| `nav_context` | object | no | Back-link label and URL for environment return flow |
**Rules**:
- The collection route is `/admin/workspaces/{workspace}/operations`.
- The detail route is `/admin/workspaces/{workspace}/operations/{run}`.
- Explicit environment filters outside the current workspace or actor entitlement are `404`.
- Stale remembered environment filters may be discarded, but explicit hostile filters may not widen scope silently.
### 7. Searchable Destination Contract
**Persistence**: none, derived from Filament resource configuration
**Owner**: touched globally searchable resources
| Field | Type | Required | Notes |
|---|---|---|---|
| `resource_key` | string | yes | `workspace` or `managed_environment` |
| `record_title_attribute` | string | yes | Existing Filament record title attribute |
| `destination_kind` | string | yes | `view` or `edit` |
| `destination_route` | string | yes | Valid route after the workspace-first cutover |
| `global_search_enabled` | bool | yes | Search remains enabled only if destination stays valid |
**Rules**:
- `WorkspaceResource` remains searchable only if its view/edit destination stays valid.
- `TenantResource` remains searchable only if its view/edit destination stays valid after the environment route move.
- Touched surfaces that cannot satisfy Filaments view/edit rule must be disabled from global search in the same slice.
## Route Invariants
- Public operator route families after the cutover are rooted at `/admin/workspaces/{workspace}`.
- `/admin/t/{environment}` is removed, not redirected.
- `/admin/tenants/{environment}/required-permissions` is removed, not redirected.
- `/admin/w/{workspace}/managed-tenants` is removed, not redirected.
- `/admin/operations` and `/admin/operations/{run}` are removed, not redirected.
- Shared builders and helpers must stop emitting `panel: 'tenant'` for touched operator destinations.
- `Workspace` is the only Filament tenant for operator routing; `ManagedEnvironment` is nested route context only.
## State Transitions
1. `NoWorkspaceSelected` -> `ChooseWorkspace`
2. `WorkspaceSelected` -> `WorkspaceDashboard`
3. `WorkspaceDashboard` -> `EnvironmentChooser`
4. `EnvironmentChooser` -> `ManagedEnvironmentDashboard`
5. `ManagedEnvironmentDashboard` -> `EnvironmentScopedPage` or `WorkspaceOperationsScope`
6. `WorkspaceSwitch` -> clear cross-workspace environment context before rendering the new workspace dashboard or chooser
7. `LegacyEnvironmentRouteRequested` -> `NotFound`
8. `LegacyWorkspaceChooserRequested` -> `NotFound`
9. `LegacyOperationsRouteRequested` -> `NotFound`
## Deferred Boundaries
- No new provider connection/profile entity or abstraction is introduced here.
- No artifact ownership retargeting is introduced here.
- No RBAC role or capability family change is introduced here.
- No UI copy neutralization or localization rewrite is introduced here.
- No quality-gate pack or no-legacy automation beyond the local grep/guard proof is introduced here.