# 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 Filament’s 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.