# Research: Filament Workspace Tenancy & Environment Routing Cutover

**Date**: 2026-05-07  
**Branch**: `280-workspace-tenancy-environment-routing`

## Decision 1: Collapse public operator routing into the existing `admin` panel

- **Decision**: `AdminPanelProvider` remains the only operator-facing Filament panel, and `Workspace` becomes the only Filament tenant for operator admin routing. `TenantPanelProvider` stops owning public operator routes.
- **Rationale**: verified repo seams show `AdminPanelProvider` already owns `/admin`, `ChooseWorkspace`, `ChooseTenant`, `WorkspaceOverview`, and the current workspace-scoped operations hub, while `TenantPanelProvider` exists only to keep the temporary `/admin/t` shell alive. Filament v5 panel configuration on Laravel 12 still expects provider registration in `apps/platform/bootstrap/providers.php`, so the narrowest truthful cutover is to remove the second operator panel rather than preserve it as a redirect shell.
- **Alternatives considered**:
  - Keep `TenantPanelProvider` as a redirect-only shell: rejected because it preserves the same split public truth and adds compatibility behavior the spec forbids.
  - Introduce nested environment Filament tenancy: rejected because `ManagedEnvironment` should remain nested route context, not a second panel tenancy.

## Decision 2: Make the workspace-scoped environment chooser the canonical public environment entry surface

- **Decision**: the canonical environment chooser route lives at `/admin/workspaces/{workspace}/environments`, reusing the current workspace-bound `ManagedTenantsLanding` surface. `ChooseTenant` remains an implementation seam to absorb or delegate selection logic, but it should not survive as a second canonical public chooser route, and `/admin/w/{workspace}/managed-tenants` is retired with no redirect or alias.
- **Rationale**: `ManagedTenantsLanding` already binds a `Workspace` route parameter and lists accessible `ManagedEnvironment` records for that workspace. `ChooseTenant` currently mixes workspace recovery, selection, and redirection into the temporary tenant panel. Keeping the workspace-scoped landing as the public chooser keeps the route language aligned with the new workspace-first contract.
- **Alternatives considered**:
  - Keep both `ChooseTenant` and `ManagedTenantsLanding` public: rejected because it preserves two chooser URLs for the same operator decision.
  - Continue auto-branching from workspace selection directly into one environment dashboard: rejected because the spec treats the workspace dashboard and environment chooser as explicit decision surfaces, not hidden branching behavior.

## Decision 3: Keep `/admin` tied to workspace selection or the workspace dashboard

- **Decision**: direct `/admin` requests resolve only to workspace selection or the canonical workspace dashboard. Environment entry becomes explicit through the workspace dashboard and environment chooser instead of using `/admin` as a hidden second environment dashboard route.
- **Rationale**: the spec requires `/admin` to remain the operator entrypoint only, not a second canonical environment route. This keeps the workspace dashboard as the primary decision surface and removes the need to infer operator scope from a remembered environment.
- **Alternatives considered**:
  - Keep `/admin` auto-redirecting into one environment when possible: rejected because it weakens the workspace-first shell and makes route truth dependent on remembered tenant context.
  - Leave `/admin` ambiguous between workspace and environment dashboards: rejected because it preserves the current split truth.

## Decision 4: Reuse the existing workspace and environment dashboards under new route ownership

- **Decision**: `WorkspaceOverview` plus `WorkspaceOverviewBuilder` remain the workspace dashboard, and `TenantDashboard` plus `TenantDashboardSummaryBuilder` remain the managed-environment dashboard. The slice changes route ownership, breadcrumbs, context-bar signals, and deep links only.
- **Rationale**: current repo truth already has the signal builders and widget families needed for this slice. The missing piece is not new dashboard content, but the temporary panel and route shell around it.
- **Alternatives considered**:
  - Build new dashboard pages for workspace-first routing: rejected because it would duplicate existing signal ownership and absorb UI work the spec explicitly defers.
  - Refactor builder ownership now: rejected because provider extraction and copy neutralization are deferred to later specs.

## Decision 5: Move operations into a workspace-first route family through shared navigation builders

- **Decision**: `/admin/workspaces/{workspace}/operations` and `/admin/workspaces/{workspace}/operations/{run}` become the canonical operations routes, and `OperationRunLinks`, `RelatedNavigationResolver`, and `Monitoring\Operations` remain the only shared seams for retargeting collection/detail URLs, environment filters, and back-navigation context. The legacy `/admin/operations` family is retired with no redirect or alias.
- **Rationale**: `Monitoring\Operations` already models `managed_environment_id`, `tenant_scope`, `problemClass`, `activeTab`, and navigation context as workspace-scoped state. The current defect is the public route family and back-link language, not the monitoring page itself.
- **Alternatives considered**:
  - Keep `/admin/operations` as the canonical route: rejected because the spec explicitly requires the workspace-first operations family.
  - Add environment-local operations pages: rejected because the operations hub is already the canonical shared monitoring surface.

## Decision 6: Update middleware, route categorization, and panel-context resolution together

- **Decision**: `EnsureWorkspaceSelected`, `EnsureFilamentTenantSelected`, `TenantPageCategory`, `ResolvesPanelTenantContext`, and `WorkspaceRedirectResolver` must move to the workspace-first environment family in the same implementation slice.
- **Rationale**: those seams currently special-case `/admin/t`, `/admin/tenants/{environment}`, workspace-canonical `/admin/operations`, and current panel ID. Partial updates would leave stale remembered environment context, wrong 404/403 handling, or mixed breadcrumb language.
- **Alternatives considered**:
  - Route redirects only: rejected because redirects would preserve the old route language and leave classifier logic stale.
  - Query-parameter-only environment context: rejected because the spec requires canonical workspace-first environment routes, not implicit query-driven context.

## Decision 7: Keep global search truthful only where destinations remain valid

- **Decision**: the touched search-eligible surfaces are `WorkspaceResource` and `TenantResource`. Each remains globally searchable only if it still has a valid view or edit destination under the workspace-first route contract; otherwise search is disabled in the same slice.
- **Rationale**: Filament v5 global search requires a resource to have a view or edit page in order to produce valid results. Current repo truth shows most other touched resources are already `isGloballySearchable = false`, so the search review surface is small and explicit.
- **Alternatives considered**:
  - Leave current global search behavior unchanged and fix later: rejected because it can leave broken results or inaccurate hints immediately after the route cutover.

## Decision 8: Prove the cutover with focused feature coverage, one browser smoke, and grep/guard checks

- **Decision**: use focused feature tests for routing and authorization, one browser smoke for the workspace-to-environment-to-operations path, and explicit grep/guard checks for `/admin/t` and `panel: 'tenant'` regressions.
- **Rationale**: Filament documentation emphasizes testing authorization and Livewire page behavior end to end, and the riskiest regression here is the live entry flow across chooser, dashboard, and operations surfaces. One narrow browser smoke is enough; broader browser or governance suites would be disproportionate.
- **Alternatives considered**:
  - Feature tests only: rejected because they would not prove the live chooser-to-dashboard flow on the surviving panel.
  - Broad browser suite: rejected because it would create a new heavy cost center for a routing cutover slice.

## Final Research Outcome

- The final operator runtime should have one public panel and one public route language rooted in workspace scope.
- The workspace-scoped environment chooser should become the public environment entry surface, with `ChooseTenant` absorbed into the same flow rather than preserved as a second public chooser URL.
- The legacy chooser route `/admin/w/{workspace}/managed-tenants` and the legacy operations family `/admin/operations` must not survive as redirects, aliases, or hidden fallback readers.
- The workspace dashboard and managed-environment dashboard should be reused as-is under new route ownership.
- Operations routing should move through the shared builders and preserve explicit environment scope via `managed_environment_id` and back-link context.
- Middleware, page categorization, and panel-context resolution must change together so deny-as-not-found and remembered-context semantics stay correct.
- Global search must remain truthful for `WorkspaceResource` and `TenantResource` or be disabled in the same slice.
- The narrowest honest proof is feature coverage, one browser smoke, and grep/guard checks.