TenantAtlas/specs/280-workspace-tenancy-environment-routing/spec.md
ahmido 670c46dedd Spec 280: prepare workspace tenancy and environment routing cutover (#336)
## Summary
- add the implementation-ready spec-prep artifacts for Spec 280: Filament Workspace Tenancy & Environment Routing Cutover
- define the bounded scope, rollout constraints, route contract, and validation plan for the workspace-first routing cutover
- update the generated Copilot agent context for the active feature branch

## Testing
- not run; this branch adds spec-prep artifacts only and does not change application code

## Notes
- no application runtime, database, or frontend code changes are included in this PR
- target base branch requested: `platform-dev`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #336
2026-05-07 10:20:43 +00:00

395 lines
54 KiB
Markdown

# Feature Specification: Filament Workspace Tenancy & Environment Routing Cutover
**Feature Branch**: `280-workspace-tenancy-environment-routing`
**Created**: 2026-05-07
**Status**: Ready
**Input**: User description: "Work only in /Users/ahmeddarrazi/Documents/projects/wt-plattform on branch 280-workspace-tenancy-environment-routing. Update only spec-prep artifacts, no application code. Fill /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/280-workspace-tenancy-environment-routing/spec.md as the implementation-ready spec for candidate `280 - Filament Workspace Tenancy & Environment Routing Cutover` from docs/product/spec-candidates.md and docs/product/roadmap.md."
## Spec Candidate Check
- **Problem**: TenantPilot's operator runtime is still split between the workspace-scoped admin panel under `/admin` and the temporary ManagedEnvironment-scoped Filament panel under `/admin/t`. That leaves the public route contract, breadcrumbs, chooser flow, middleware, and deep-link builders behind the core model cutover completed in Spec `279`.
- **Today's failure**: Verified repo seams such as `TenantPanelProvider`, `ChooseTenant`, `WorkspaceRedirectResolver`, `EnsureWorkspaceSelected`, `EnsureFilamentTenantSelected`, `TenantPageCategory`, `OperationRunLinks`, and many `panel: 'tenant'` links still route operators into `/admin/t/{environment}` or `/admin/tenants/{environment}`. Operators move from a workspace surface into a second panel with a different route language, while current operations remain canonical at workspace scope. The product therefore still communicates two different truths about where environment work actually lives.
- **User-visible improvement**: Operators get one workspace-first admin runtime: workspace dashboard, workspace-scoped environment chooser, managed-environment dashboard, workspace-scoped operations hub, and breadcrumbs that consistently read `Workspace -> Managed Environment -> domain page`.
- **Smallest enterprise-capable version**: Collapse the temporary `/admin/t` panel into the existing `/admin` admin panel, configure `Workspace` as the only Filament tenant, move environment pages under `/admin/workspaces/{workspace}/environments/{environment}`, make `/admin/workspaces/{workspace}/operations` the canonical operations route, reuse the existing workspace and environment dashboard builders, and remove the old route families without adding aliases or compatibility redirects.
- **Explicit non-goals**: No provider extraction, no governance-artifact retargeting, no provider-neutral capability registry, no provider-neutral artifact taxonomy, no RBAC redesign, no UI copy neutralization beyond route/breadcrumb/context truth needed by this slice, no quality-gate pack beyond feature-local routing/tenancy proof, no customer-portal cutover, no second Filament tenancy at the environment level, and no compatibility routes or dual-panel end state.
- **Permanent complexity imported**: One admin-panel tenancy flip from temporary environment-panel ownership to workspace tenancy, one canonical workspace-first environment route family, one route/breadcrumb/current-context contract update across chooser and deep-link seams, and focused feature/browser plus guard coverage. No new persistence, enum family, abstraction layer, or asset system is introduced.
- **Why now**: Spec `279` already changed the core managed-target noun to `ManagedEnvironment`, and the verified controlling seam still shows the old shell in `TenantPanelProvider` with `path('admin/t')` and `tenant(ManagedEnvironment::class, slugAttribute: 'slug')`. Until `280` closes that shell, every later reserved cutover slice (`281`-`287`) sits on an intentionally temporary panel and route contract.
- **Why not local**: This is not one page or one redirect. The temporary shell is encoded in provider registration, chooser flow, middleware, route categorization, current-context resolution, dashboard links, operations links, and resource URLs. A local fix would create the same hidden compatibility layer that the candidate and constitution reject.
- **Approval class**: Core Enterprise
- **Red flags triggered**: Many touched operator surfaces and a foundation-sounding cutover. Defense: the slice explicitly reuses existing workspace and environment dashboard builders, removes a temporary panel instead of adding framework machinery, forbids compatibility routes, and defers the remaining provider/artifact/RBAC/copy/quality follow-up to reserved Specs `281`-`287`.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12**
- **Decision**: approve
## Spec Scope Fields
- **Scope**: workspace
- **Primary Routes**:
- `/admin` remains the operator entrypoint only and resolves to workspace chooser or the currently selected workspace dashboard; it is not a second canonical environment dashboard route
- `/admin/workspaces/{workspace}` becomes the canonical workspace dashboard route for the active workspace
- `/admin/workspaces/{workspace}/environments` becomes the canonical workspace-scoped environment chooser route
- `/admin/workspaces/{workspace}/environments/{environment}` becomes the canonical managed-environment dashboard route
- `/admin/workspaces/{workspace}/environments/{environment}/{domain...}` becomes the canonical family for environment-scoped resources, diagnostics, and detail pages that today resolve through `panel: 'tenant'` or `/admin/tenants/{environment}`
- `/admin/workspaces/{workspace}/operations` and `/admin/workspaces/{workspace}/operations/{run}` become the canonical operations collection/detail routes
- `/admin/w/{workspace}/managed-tenants`, `/admin/operations`, and `/admin/operations/{run}` are removed rather than retained as redirects, aliases, or hidden fallback readers
- `/admin/t/{environment}` and `/admin/tenants/{environment}/required-permissions` are removed rather than retained as aliases
- **Data Ownership**:
- `Workspace` remains the workspace-owned root context and becomes the only Filament tenant for operator admin routing
- `ManagedEnvironment` remains the managed target nested inside a workspace and is resolved from workspace-first route parameters and current-context helpers, not from a second Filament tenant registration
- no table ownership, artifact ownership, provider-profile ownership, or RBAC ownership changes are introduced in this slice
- **RBAC**:
- workspace membership remains the first isolation boundary
- managed-environment access remains the second isolation boundary
- wrong-workspace or wrong-environment access remains `404`
- in-scope actors missing capability remain `403`
- no new role family, capability family, or authorization plane is introduced
Canonical-view handling for workspace-scoped monitoring surfaces:
- **Default filter behavior when tenant-context is active**: when an operator enters a workspace-scoped canonical view from a managed-environment dashboard or environment-scoped page, the canonical view opens prefiltered to that `managed_environment_id` inside the current workspace; widening to all environments remains explicit.
- **Explicit entitlement checks preventing cross-tenant leakage**: route-bound `{workspace}` and `{environment}` values must match each other and the current actor's entitlements. Explicit environment query hints outside the current workspace or outside the actor's membership resolve as `404`, while stale remembered filters are discarded instead of leaking or cross-loading another environment.
## Cross-Cutting / Shared Pattern Reuse
- **Cross-cutting feature?**: yes
- **Interaction class(es)**: navigation entry points, context selection, dashboard signals, breadcrumbs, page context bars, workspace-to-environment drillthroughs, operations deep links, and route builders
- **Systems touched**: `AdminPanelProvider`, `TenantPanelProvider`, `ChooseWorkspace`, `ChooseTenant`, `WorkspaceOverview`, `TenantDashboard`, `Monitoring\Operations`, `WorkspaceRedirectResolver`, `EnsureWorkspaceSelected`, `EnsureFilamentTenantSelected`, `ResolvesPanelTenantContext`, `TenantPageCategory`, `WorkspaceOverviewBuilder`, `TenantDashboardSummaryBuilder`, `OperationRunLinks`, and `RelatedNavigationResolver`
- **Existing pattern(s) to extend**: the existing `/admin` workspace panel, current workspace chooser flow, current `WorkspaceOverview` dashboard signals, current `TenantDashboard` environment signals, and the current tenant-safe operations link builders
- **Shared contract / presenter / builder / renderer to reuse**: `WorkspaceOverviewBuilder`, `TenantDashboardSummaryBuilder`, `WorkspaceContext`, `WorkspaceRedirectResolver`, `OperationRunLinks`, `RelatedNavigationResolver`, and the existing Filament page/resource action-surface contracts
- **Why the existing shared path is sufficient or insufficient**: these shared seams already own the user-facing signals and links that the final routing contract needs. What is insufficient today is not the dashboard or monitoring content itself, but the temporary second-panel route ownership and the mixed `/admin/t` versus `/admin/tenants` public path language.
- **Allowed deviation and why**: none. This slice removes the temporary panel and route family instead of introducing another exception.
- **Consistency impact**: workspace chooser, environment chooser, dashboard links, operations links, breadcrumbs, context bar signals, and page-level deep links must all speak the same workspace-first route language and the same `Workspace -> Managed Environment -> domain page` hierarchy.
- **Review focus**: reviewers must verify that `WorkspaceOverview` and `TenantDashboard` are reused rather than replaced, that `OperationRunLinks` and related builders stop emitting `panel: 'tenant'` or `/admin/t` URLs, and that no second route language or compatibility redirect remains.
## OperationRun UX Impact
- **Touches OperationRun start/completion/link UX?**: yes, link semantics only
- **Shared OperationRun UX contract/layer reused**: `OperationRunLinks`, `RelatedNavigationResolver`, the existing `Monitoring\Operations` page, and the existing canonical navigation context helpers
- **Delegated start/completion UX behaviors**: workspace-safe route resolution for operations index/detail, workspace-to-environment back-link context, and environment-prefiltered operations drillthrough via `managed_environment_id`; no new toast, queue, dedupe, terminal-notification, or artifact-link behavior is introduced
- **Local surface-owned behavior that remains**: environment dashboards and environment-scoped pages only supply the filter context and back-navigation label for the existing operations surface
- **Queued DB-notification policy**: `N/A`
- **Terminal notification path**: `N/A`
- **Exception required?**: none
## Provider Boundary / Platform Core Check
- **Shared provider/platform boundary touched?**: yes
- **Boundary classification**: mixed
- **Seams affected**: panel tenancy, route nouns, chooser wording, page-category classification, breadcrumb context, current-context resolution, and deep-link builders that still encode temporary environment-panel ownership
- **Neutral platform terms preserved or introduced**: `workspace`, `managed environment`, `environment`, `operations`, `findings`, `backup sets`, `evidence`, and `required permissions`
- **Provider-specific semantics retained and why**: provider-owned Microsoft terms may remain in untouched provider copy, metadata, or class names such as `TenantResource` until Specs `281` and `286` handle provider extraction and copy neutralization. This slice only requires that routing, breadcrumbs, panel tenancy, chooser labels, and context signals touched by the cutover become workspace/environment truthful.
- **Why this does not deepen provider coupling accidentally**: the slice removes a provider-shaped public shell instead of entrenching it. `Workspace` becomes the Filament tenant, `ManagedEnvironment` remains nested context, and no provider-profile fields or Microsoft-specific routing semantics are added to the platform-core route contract.
- **Follow-up path**: Spec `281` for provider-profile extraction, Spec `282` for artifact retargeting, Spec `285` for workspace-first RBAC scope follow-through, Spec `286` for copy neutralization, and Spec `287` for explicit no-legacy enforcement
## UI / Surface Guardrail Impact
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note |
|---|---|---|---|---|---|---|
| Workspace dashboard under the canonical workspace route | yes | Native Filament page plus shared dashboard builders | navigation, dashboard signals, body CTA links | page, URL, summary state | no | Reuses `WorkspaceOverview` and `WorkspaceOverviewBuilder` rather than introducing a second workspace summary surface |
| Workspace-scoped environment chooser | yes | Native Filament simple page | context selection, navigation entry point | page, URL, remembered context | no | Reuses the existing chooser flow instead of adding a second environment selector |
| Managed-environment dashboard under the workspace-first route | yes | Native Filament dashboard plus shared widgets/builders | dashboard signals, header actions, page CTAs | page, URL, detail state | no | Reuses `TenantDashboard` and its current widget/action family; routing and breadcrumbs change, not the signal source |
| Workspace operations hub under `/admin/workspaces/{workspace}/operations` | yes | Native Filament monitoring page | operations links, back links, filter context | page, table, URL/query | no | Reuses the existing operations surface and moves only the public route ownership/context contract |
| Environment-scoped page family migrated off `/admin/t` and `/admin/tenants/{environment}` | yes | Native Filament resources/pages | navigation, breadcrumbs, current-context shell | page, detail, URL/query | no | Local action contracts stay intact; this slice changes route ownership and breadcrumb shell only |
## Decision-First Surface Role
| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction |
|---|---|---|---|---|---|---|---|
| Workspace dashboard under the canonical workspace route | Primary Decision Surface | Operator scans one workspace to decide which environment or workflow needs attention next | workspace-wide signal counts, attention items, recent operations, and quick actions | deeper environment detail remains one click away on the environment dashboard or operations hub | Primary because it is the workspace landing surface after workspace selection | Follows workspace-first operating flow instead of dropping straight into one environment panel | Removes the current panel split and makes workspace scope explicit before environment work begins |
| Workspace-scoped environment chooser | Primary Decision Surface | Operator chooses the managed environment they intend to act in | environment identity, lifecycle posture, and current workspace context | deeper environment diagnostics stay on the environment dashboard | Primary because environment selection is the next explicit decision after workspace selection | Keeps selection inside the same workspace panel instead of jumping into a second panel | Removes the cognitive break between workspace selection and environment work |
| Managed-environment dashboard under the workspace-first route | Secondary Context Surface | Operator confirms the chosen environment and decides which domain page to open next | environment posture, chips, recommended action, and dashboard signals | raw/support detail stays where it already belongs on downstream pages | Secondary because it is an environment workbench, not the workspace-wide landing decision surface | Aligns with today's dashboard-driven environment workflow, but under truthful workspace-first routing | Reduces wrong-environment actions by keeping workspace and environment context visible together |
| Workspace operations hub under `/admin/workspaces/{workspace}/operations` | Primary Decision Surface | Operator reviews queued and completed operations for one workspace, optionally narrowed to one environment | current workspace, active environment filter if present, operation status/outcome, and canonical back link | deeper run detail stays on the run viewer | Primary because operations already serve as the canonical monitoring surface | Preserves the existing monitoring workflow while removing the need to infer environment context from a second panel | Reduces duplicate monitoring entry points and keeps widening/narrowing explicit |
| Environment-scoped page family migrated off `/admin/t` and `/admin/tenants/{environment}` | Secondary Context Surface | Operator performs the chosen domain task inside one managed environment | breadcrumb/context shell plus the existing page-local operational truth | existing diagnostics remain page-local and unchanged | Secondary because the decision is made on dashboard or operations surfaces; these pages carry the work forward | Preserves each page's current workflow contract while making its scope truthful in navigation | Removes path-language drift without creating new page-local decision systems |
## Audience-Aware Disclosure
| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention |
|---|---|---|---|---|---|---|---|
| Workspace dashboard under the canonical workspace route | operator-MSP, support-platform | workspace-wide attention, recent operations, and one next-action entry point | per-environment follow-up remains secondary | raw evidence stays on downstream pages | `Open environment` or `Open operations` depending on the top signal | raw evidence and low-level diagnostics remain downstream | workspace summary stays at workspace level and does not restate environment dashboards |
| Workspace-scoped environment chooser | operator-MSP, support-platform | current workspace plus selectable managed environments | lifecycle posture only | provider/raw detail hidden | `Open environment` | provider-specific raw metadata | chooser states selection truth once, then hands off to the environment dashboard |
| Managed-environment dashboard under the workspace-first route | operator-MSP, support-platform | environment summary, posture, and recommended next action | page-local diagnostics, support request detail, and additional widgets | raw evidence stays on dedicated downstream surfaces | primary follow-up action | support/raw evidence remains downstream and capability-gated where already required | dashboard shows the environment summary once and uses links for deeper evidence instead of repeating it |
| Workspace operations hub under `/admin/workspaces/{workspace}/operations` | operator-MSP, support-platform | workspace scope, optional environment filter, and operation follow-up state | run-level diagnostics remain on the run viewer | raw payloads remain on detail pages or related evidence surfaces | `Open operation` or `Back to environment` | low-level run evidence stays in detail | the hub states scope once and uses detail pages for deep evidence |
| Environment-scoped page family migrated off `/admin/t` and `/admin/tenants/{environment}` | operator-MSP, support-platform | breadcrumb scope plus each page's existing core truth | existing diagnostics stay on the page | support/raw evidence remains unchanged and page-local | the page's existing primary action | unrelated raw/support detail remains gated as today | the new shell adds context instead of duplicating page-local summaries |
## UI/UX Surface Classification
| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Workspace dashboard under the canonical workspace route | Dashboard / Context / Summary | Workspace landing dashboard | Open one environment or the operations hub | body CTA cards and quick actions | forbidden | body-level quick actions only | none | `/admin/workspaces/{workspace}` | `/admin/workspaces/{workspace}` | active workspace | Workspace | workspace-wide attention and recent operations | none |
| Workspace-scoped environment chooser | Selector / Context / Entry | Workspace environment selector | Open the intended managed environment | environment card/button | allowed | workspace switch remains secondary | none | `/admin/workspaces/{workspace}/environments` | `/admin/workspaces/{workspace}/environments/{environment}` | active workspace plus environment identity | Managed Environment | selectable environment identity and posture | none |
| Managed-environment dashboard under the workspace-first route | Dashboard / Detail / Workflow hub | Environment dashboard | Follow the recommended domain action | dashboard CTA from widget or header action | forbidden | grouped header secondary actions plus body links | none introduced by this slice | `/admin/workspaces/{workspace}/environments` | `/admin/workspaces/{workspace}/environments/{environment}` | active workspace and active managed environment | Managed Environment | environment posture, next action, and dashboard signals | none |
| Workspace operations hub under `/admin/workspaces/{workspace}/operations` | List / Table / Monitoring | Workspace monitoring hub | Inspect a run or widen/narrow environment scope | clickable row to run detail | required | header back-link and filter actions | none | `/admin/workspaces/{workspace}/operations` | `/admin/workspaces/{workspace}/operations/{run}` | active workspace and optional managed-environment filter | Operations / Operation | current scope, run state, and follow-up truth | none |
| Environment-scoped page family migrated off `/admin/t` and `/admin/tenants/{environment}` | List / Detail / Resource family | Workspace-first environment resources and diagnostics | Continue the chosen domain workflow | preserve each surface's current inspect model | preserve current contract | preserve current contract | preserve current contract | `/admin/workspaces/{workspace}/environments/{environment}/{domain}` | `/admin/workspaces/{workspace}/environments/{environment}/{domain}/{record?}` | breadcrumb `Workspace -> Managed Environment -> page` | Existing domain noun | active workspace/environment context plus each page's existing truth | none |
## Operator Surface Contract
| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|---|
| Workspace dashboard under the canonical workspace route | Workspace operator | Decide which environment or workspace-wide workflow needs attention first | Dashboard | What in this workspace needs my attention next? | workspace-wide attention, recent operations, quick actions | deeper environment evidence stays downstream | workspace attention, run activity, recovery posture | none | Open environment, Open operations | none |
| Workspace-scoped environment chooser | Workspace operator | Decide which managed environment to enter | Context-selection page | Which managed environment am I about to work in? | environment identity, posture, workspace context | deeper diagnostics remain on the dashboard | lifecycle posture, access posture | none | Open environment | none |
| Managed-environment dashboard under the workspace-first route | Environment operator | Decide which environment-scoped page to open next | Dashboard | What in this environment needs action now? | posture, dashboard chips, recommended action, summary widgets | support diagnostics and deeper evidence remain downstream | environment posture, readiness, attention state | existing page-local semantics only | Primary follow-up, More, Request support | none added by this slice |
| Workspace operations hub under `/admin/workspaces/{workspace}/operations` | Monitoring operator | Review runs across the workspace or one environment | Monitoring table/detail | Which operations need follow-up, and for which environment? | current scope, optional environment filter, run status/outcome, back link | run detail, evidence, and related links remain secondary | execution status, outcome, scope | read-only | Open operation, Back to environment, Show all environments | none |
| Environment-scoped page family migrated off `/admin/t` and `/admin/tenants/{environment}` | Environment operator | Perform the selected domain workflow in one environment | Resource/detail family | Am I acting in the correct workspace and managed environment? | breadcrumb scope and existing page truth | page-local diagnostics unchanged | preserve current domain-specific status families | preserve current page-local mutation semantics | preserve current page-local primary action | preserve current page-local dangerous actions |
## Proportionality Review
- **New source of truth?**: no
- **New persisted entity/table/artifact?**: no
- **New abstraction?**: no
- **New enum/state/reason family?**: no
- **New cross-domain UI framework/taxonomy?**: no
- **Current operator problem**: the operator runtime still communicates two conflicting truths: workspace-wide work begins on `/admin`, but environment work still lives on a temporary `/admin/t` panel and a second `/admin/tenants/{environment}` route family.
- **Existing structure is insufficient because**: the current structure requires special-casing `/admin/t` and `/admin/tenants` in provider setup, middleware, route categorization, chooser flow, and deep-link builders. As long as that split remains, later provider/artifact/RBAC cutovers would be forced to build on a known temporary shell.
- **Narrowest correct implementation**: reuse the current admin panel, workspace dashboard, environment dashboard, chooser flow, and operations surface, and change only the tenancy and public route ownership needed to remove the temporary shell.
- **Ownership cost**: route and deep-link retargeting across chooser, middleware, builders, breadcrumbs, and tests; removal of one panel provider from the operator runtime; and focused browser/feature coverage to keep the route contract honest.
- **Alternative intentionally rejected**: leaving the `/admin/t` panel in place with redirects, keeping `/admin/tenants/{environment}` as a second route family, or adding nested/dual Filament tenancy. Those options preserve the temporary shell instead of closing it.
- **Release truth**: current-release truth; this slice removes temporary runtime structure rather than preparing future framework flexibility
### Compatibility posture
This feature assumes a pre-production environment.
Backward compatibility, legacy aliases, redirect shims, dual-panel ownership, dual-context restore paths, and compatibility-specific tests are out of scope unless explicitly required by this spec.
Canonical replacement is preferred over preservation.
## Testing / Lane / Runtime Impact
- **Test purpose / classification**: Feature, Browser
- **Validation lane(s)**: fast-feedback, confidence, browser
- **Why this classification and these lanes are sufficient**: this slice changes panel tenancy, public routes, chooser flow, breadcrumbs, deep-link builders, and workspace/environment authorization semantics. Feature tests prove route ownership, filter context, and deny-as-not-found behavior; one narrow browser smoke proves the chooser-to-dashboard-to-operations flow still works end to end on the surviving admin panel.
- **New or expanded test families**: one workspace-tenancy/routing feature family, one environment-routing/breadcrumb feature family, one operations canonical-context feature family, one legacy-route-removal or grep/guard family, and one narrow browser smoke for the workspace-to-environment flow
- **Fixture / helper cost impact**: moderate. Tests must seed a workspace plus managed environments and reuse existing workspace/member/environment context helpers. The slice should not introduce new default-heavy fixtures, providers, or session helpers beyond what current workspace and managed-environment tests already require.
- **Heavy-family visibility / justification**: one browser smoke only. No new heavy-governance or broad browser family is justified.
- **Special surface test profile**: standard-native-filament, global-context-shell, monitoring-state-page
- **Standard-native relief or required special coverage**: standard feature coverage is sufficient for provider registration, tenancy switch, route removal, and breadcrumb semantics. One browser smoke is required because the cutover changes the live operator entry flow across chooser, dashboard, and operations surfaces.
- **Reviewer handoff**: reviewers must verify that `AdminPanelProvider` becomes the only operator panel, any provider-registration change remains in `apps/platform/bootstrap/providers.php`, `TenantPanelProvider` and `/admin/t` cease to own public operator routing, `ChooseTenant` and `WorkspaceRedirectResolver` stop emitting `panel: 'tenant'` destinations, `OperationRunLinks` and related navigation open the workspace operations hub with explicit environment filters, `TenantPageCategory` and the two middleware seams no longer treat `/admin/t` or `/admin/tenants/{environment}` as active route languages, `WorkspaceOverview` and `TenantDashboard` are reused rather than replaced, `WorkspaceResource` and `TenantResource` keep valid view/edit search destinations or disable search if that contract cannot be kept, destructive actions touched by the route move still require confirmation and server authorization, Livewire remains v4, and no new asset registration/deploy step appears.
- **Budget / baseline / trend impact**: moderate feature-local increase only
- **Escalation needed**: none
- **Active feature PR close-out entry**: Guardrail
- **Planned validation commands**:
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Feature/Workspace/WorkspaceFilamentTenancyCutoverTest.php tests/Feature/ManagedEnvironment/WorkspaceFirstEnvironmentRoutingTest.php tests/Feature/Monitoring/WorkspaceOperationsEnvironmentContextTest.php tests/Feature/Navigation/WorkspaceEnvironmentBreadcrumbsTest.php tests/Feature/Guards/LegacyAdminTenantRouteRemovalGuardTest.php)`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Browser/Spec280WorkspaceTenancyEnvironmentRoutingSmokeTest.php)`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail bin pint --dirty --format agent)`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings '/admin/t/' "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/routes" "$REPO_ROOT/apps/platform/bootstrap"`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings '/admin/tenants/' "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/routes" "$REPO_ROOT/apps/platform/bootstrap"`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings '/admin/w/' "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/routes" "$REPO_ROOT/apps/platform/bootstrap"`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings '/admin/operations' "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/routes" "$REPO_ROOT/apps/platform/bootstrap"`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings "panel: 'tenant'" "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/routes" "$REPO_ROOT/apps/platform/bootstrap"`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings 'TenantPanelProvider::class' "$REPO_ROOT/apps/platform/bootstrap/providers.php"`
## Scope Boundaries *(required for this slice)*
### In Scope
- make `Workspace` the only Filament tenant for the operator admin runtime
- collapse the operator-facing `/admin/t` panel into the existing `/admin` admin panel
- remove the public `/admin/t/{environment}` route family with no compatibility alias
- remove the public `/admin/tenants/{environment}/required-permissions` family in favor of the workspace-first environment route family
- introduce the canonical workspace-first environment route family rooted at `/admin/workspaces/{workspace}/environments/{environment}`
- reuse `WorkspaceOverview` as the workspace dashboard and `TenantDashboard` as the managed-environment dashboard under the new route ownership
- make `/admin/workspaces/{workspace}/operations` the canonical operations route and thread environment pages into it through explicit filters and back-navigation context
- retarget chooser flow, middleware, route categorization, deep-link builders, breadcrumbs, and page-level context signals to the new route contract
- ensure touched globally searchable resources preserve a valid view/edit destination or are disabled from global search in the same slice
### Non-Goals
- provider connection, provider scope, or Microsoft profile extraction owned by Spec `281`
- governance artifact retargeting to `ManagedEnvironment` owned by Spec `282`
- provider capability registry or provider-neutral artifact taxonomy owned by Specs `283` and `284`
- workspace-first RBAC redesign or capability-family changes owned by Spec `285`
- UI copy, IA, and localization neutralization beyond the route/breadcrumb/context truth needed here, owned by Spec `286`
- cutover quality-gate pack work beyond the routing/tenancy slice proof needed here, owned by Spec `287`
- customer portal, website, package-engine, or guided-operations work
- compatibility routes, aliases, redirects that preserve `/admin/t`, dual panel ownership, dual-read/dual-write route logic, or hidden absorption of later reserved specs
## Assumptions
- Spec `279` already completed the core `ManagedEnvironment` cutover and remains prerequisite context only.
- `AdminPanelProvider` remains the long-lived operator panel under `/admin`, and removing `TenantPanelProvider` from operator routing is acceptable in this pre-production environment.
- The current workspace and environment dashboards already provide the operator signal content needed for the final route contract; `280` relocates and rekeys them instead of redesigning their signal models.
- Internal class names or untouched provider copy may still use `tenant` until later reserved slices, but routing, breadcrumbs, chooser labels, and operator-facing context signals touched by this slice must be workspace/environment truthful.
## Risks
- Hidden `/admin/t` or `panel: 'tenant'` references in builders, widgets, or tests could leave a partial second route language behind after the cutover.
- Operations deep links and back links could lose environment scope or workspace safety if they are retargeted page by page instead of through the shared link builders.
- Collapsing panel ownership could break current context bootstrap if middleware and route categorization are not updated together.
- The slice could sprawl into copy cleanup or RBAC redesign if reviewers do not hold the scope boundary against Specs `285` and `286`.
## Candidate Selection Gate Summary
- **Selected candidate**: `280 - Filament Workspace Tenancy & Environment Routing Cutover`
- **Source locations**:
- `docs/product/spec-candidates.md` under the reserved workspace-first / provider-neutral cutover pack
- `docs/product/roadmap.md` under the same reserved cutover ordering and sequencing note
- **Why selected now**: Spec `279` already changed the core managed-target noun, and the verified controlling seam still shows the temporary panel shell in `TenantPanelProvider`. `280` is the second reserved slot and closes that temporary public routing/tenancy contract before later provider and artifact follow-up work begins.
- **Why close alternatives were deferred**:
- `281` belongs to provider connection/profile extraction, not to route collapse
- `282` belongs to artifact retargeting, not to public route ownership
- `283` and `284` belong to provider-neutral packaging follow-through, not to the immediate shell removal
- `285`, `286`, and `287` are explicit follow-up guardrail slices for RBAC, copy, and no-legacy enforcement
- the already-existing mid-260/270 packages remain context only and are not the target of this reserved cutover slot
- **Smallest viable implementation slice**: one surviving workspace-tenanted admin panel, one canonical workspace-first environment route family, one canonical workspace operations route family, and one bounded set of chooser/middleware/link/breadcrumb updates with no aliases
- **Documented deviation from raw candidate wording**: `add a workspace dashboard` and `add a managed-environment dashboard` are satisfied by re-homing the existing `WorkspaceOverview` and `TenantDashboard` surfaces under their final route ownership rather than creating new summary systems.
## Completed-Spec Guardrail Result
- `specs/265-decision-register-approval/` already exists with `Status: Ready for implementation` and remains separate context only
- `specs/267-artifact-lifecycle-retention/` already exists with `Status: Ready for implementation` and remains separate context only
- `specs/274-billing-subscription-truth/` already exists with `Status: Ready for implementation` and remains separate context only
- `specs/275-customer-facing-localization-adoption/` already exists with `Status: Draft` and remains separate context only
- `specs/276-support-access-governance/` already exists with `Status: Ready for implementation` and remains a separate prepared package
- `specs/277-stored-reports-surface/` already exists with `Status: Ready for implementation` and remains a separate prepared package
- `specs/278-cross-domain-indicator-audit/` already exists with `Status: Implemented docs-only audit artifact; ready for manual review` and remains separate historical context only
- `specs/279-workspace-managed-environment-core/` already exists with `Status: Ready with approved feature-local exception`, carries implementation-close-out history in `tasks.md`, and is prerequisite context only for this package
## Deferred Adjacent Candidates
- `281 - Provider Connection, Provider Scope & Microsoft Profile Extraction`
- `282 - Governance Artifact Retargeting to ManagedEnvironment`
- `283 - Provider Capability Registry v1`
- `284 - Provider-neutral Artifact Source Taxonomy v1`
- `285 - Workspace-first RBAC & Environment Access Scoping`
- `286 - UI Copy, IA & Localization Neutralization`
- `287 - Cutover Quality Gates & No-Legacy Enforcement`
## User Scenarios & Testing
### User Story 1 - Enter an environment without leaving the workspace admin panel (Priority: P1)
As an operator, I want to choose a workspace and then open one managed environment inside the same admin panel so I never have to cross into a second panel or learn a second route language just to work on that environment.
**Why this priority**: this is the core cutover outcome. If the chooser still lands on `/admin/t`, the temporary shell remains active and the candidate is not complete.
**Independent Test**: select a workspace, open the environment chooser, choose one managed environment, and confirm the destination is the canonical workspace-first environment dashboard rather than `/admin/t`.
**Acceptance Scenarios**:
1. **Given** an operator has selected a workspace, **When** they choose a managed environment, **Then** they land on `/admin/workspaces/{workspace}/environments/{environment}` inside the surviving admin panel.
2. **Given** an operator enters a workspace/environment URL where the environment belongs to a different workspace or is not accessible, **When** the route resolves, **Then** the app returns `404` and reveals no environment data.
---
### User Story 2 - Move from environment work into workspace operations with explicit scope (Priority: P1)
As an operator, I want environment dashboards and environment pages to open the workspace operations hub with an explicit environment filter so operations stay workspace-canonical without losing the narrower environment context I came from.
**Why this priority**: the candidate explicitly requires `/admin/workspaces/{workspace}/operations` to be canonical and environment pages to link into it via filters.
**Independent Test**: open one environment dashboard or environment page, follow its operations link, and confirm the workspace operations hub opens with the correct environment filter and return context.
**Acceptance Scenarios**:
1. **Given** an operator is on one managed-environment dashboard, **When** they open operations, **Then** the destination is `/admin/workspaces/{workspace}/operations` with that environment's filter active.
2. **Given** the operations hub is filtered to one environment, **When** the operator chooses to widen scope, **Then** the route stays workspace-canonical and the environment filter clears explicitly rather than switching panels.
---
### User Story 3 - Read workspace-wide and environment-scoped signals on the correct surfaces (Priority: P2)
As an operator, I want the workspace dashboard to summarize workspace-wide signals and the managed-environment dashboard to summarize one environment's signals so I can decide at the right scope without reconstructing context from mixed pages.
**Why this priority**: the candidate requires both dashboards, and reusing the current builders is the narrowest honest way to deliver them.
**Independent Test**: open a workspace dashboard and an environment dashboard for the same workspace, then verify the workspace view aggregates workspace-wide signals while the environment view remains scoped to one environment and shows the full breadcrumb hierarchy.
**Acceptance Scenarios**:
1. **Given** a workspace has multiple accessible environments, **When** the operator opens the workspace dashboard, **Then** it shows workspace-wide signals and recent operations rather than one environment's dashboard state.
2. **Given** the operator opens one managed-environment dashboard, **When** the page renders, **Then** the breadcrumb and context shell show `Workspace -> Managed Environment -> page` and the signals remain scoped to that one environment.
---
### User Story 4 - Keep search and authorization truthful after the route cutover (Priority: P3)
As an operator, I want globally searchable resources and direct URLs to open truthful destinations or disappear from search altogether so the route migration does not leave broken search results or unsafe hints behind.
**Why this priority**: `WorkspaceResource` and `TenantResource` are already search-eligible by repo truth, and route changes can quietly break that contract if the spec does not pin it down.
**Independent Test**: open touched global-search results and direct URLs for workspace and managed-environment surfaces, then confirm that authorized results open valid destinations and unauthorized paths remain not found.
**Acceptance Scenarios**:
1. **Given** a touched searchable resource remains globally searchable, **When** an operator opens a search result, **Then** the destination is a valid page under the canonical workspace-first route family.
2. **Given** a touched searchable surface cannot preserve a valid view or edit destination after the route move, **When** the cutover ships, **Then** that surface no longer appears in global search.
### Edge Cases
- A direct request to `/admin/t/{environment}`, `/admin/tenants/{environment}/required-permissions`, `/admin/w/{workspace}/managed-tenants`, `/admin/operations`, or `/admin/operations/{run}` must fail as not found rather than redirecting, aliasing, or silently restoring the old path family.
- If a remembered environment belongs to a different workspace than the newly selected workspace, the environment context must clear before the operator is shown any environment-bound page.
- Archived or otherwise non-selectable managed environments must not appear in the workspace-scoped chooser or resolve through the new route family.
- Explicit environment filters on workspace-canonical pages must reject inaccessible environments as `404`; stale remembered filters may be discarded, but explicit hostile hints may not quietly widen scope.
- Any touched globally searchable resource without an intact view/edit destination under the new route contract must be disabled from global search in the same implementation slice.
## Requirements
**Constitution alignment (required):** This slice changes routing, Filament tenancy, chooser flow, navigation, and deep-link behavior. It does not introduce Microsoft Graph calls, a new `OperationRun` lifecycle, or new persistence.
**Constitution alignment (XCUT-001 / PROV-001 / UI-FIL-001):** The slice must reuse the existing shared workspace dashboard, managed-environment dashboard, chooser flow, operations hub, and deep-link builders. No second panel, second route language, or local dashboard framework may appear.
**Constitution alignment (RBAC-UX):** Workspace membership stays the first boundary and managed-environment access stays the second boundary. Non-members remain `404`, in-scope capability denials remain `403`, and any touched destructive actions continue to require confirmation and server-side authorization.
**Constitution alignment (TEST-GOV-001):** Proof stays bounded to feature coverage, one narrow browser smoke, and explicit route/URL guard checks. No heavy-governance family is introduced.
### Functional Requirements
- **FR-001**: The operator admin runtime MUST configure `Workspace` as the only Filament tenant after this cutover.
- **FR-002**: The system MUST stop using `ManagedEnvironment` as a Filament tenant and MUST remove the public `/admin/t/{environment}` route family rather than retaining it as a redirect, alias, or hidden fallback.
- **FR-003**: The surviving `/admin` admin panel MUST own the canonical workspace-first environment route family rooted at `/admin/workspaces/{workspace}/environments/{environment}`.
- **FR-004**: The admin entrypoint `/admin` MUST resolve to workspace selection or the current workspace dashboard only; it MUST NOT remain a second canonical environment dashboard route.
- **FR-005**: The system MUST reuse the existing workspace overview surface as the canonical workspace dashboard and the existing managed-environment dashboard surface as the canonical environment dashboard, rather than creating replacement dashboard systems.
- **FR-006**: The workspace-scoped chooser flow MUST open the new canonical environment dashboard route and MUST clear stale cross-workspace environment context before resolving any environment-bound page.
- **FR-007**: The workspace dashboard, environment dashboard, and all environment-scoped pages touched by this slice MUST present navigation and breadcrumbs in the order `Workspace -> Managed Environment -> domain page`.
- **FR-008**: `/admin/workspaces/{workspace}/operations` MUST be the canonical public operations collection route for the active workspace.
- **FR-009**: Environment dashboards and environment-scoped pages MUST open the workspace operations hub through explicit filter/query context, not through environment-panel ownership.
- **FR-010**: The operations detail route MUST remain inside the workspace route family so run detail does not depend on the removed `/admin/t` shell.
- **FR-011**: Route builders, deep links, and related-navigation helpers that currently emit `panel: 'tenant'`, `/admin/t`, or `/admin/tenants/{environment}` URLs MUST emit only the new canonical workspace-first routes.
- **FR-012**: `TenantPageCategory`, `EnsureWorkspaceSelected`, `EnsureFilamentTenantSelected`, `ResolvesPanelTenantContext`, and related current-context seams MUST treat the workspace-first environment family as the only active environment-bound route language.
- **FR-013**: The slice MUST preserve the current `WorkspaceOverviewBuilder` and `TenantDashboardSummaryBuilder` ownership of workspace-wide and environment-scoped summary signals.
- **FR-014**: Any touched globally searchable resource MUST preserve a valid view or edit destination under the new route contract or be disabled from global search in the same slice.
- **FR-015**: `WorkspaceResource` and `TenantResource` MUST remain globally searchable only if their view/edit destinations stay valid under the cutover route ownership.
- **FR-016**: The slice MUST NOT introduce compatibility routes, aliases, dual-panel ownership, dual route readers, hidden `/admin/t` absorption, or spillover into reserved Specs `281`-`287`.
- **FR-017**: The legacy chooser route `/admin/w/{workspace}/managed-tenants` MUST be removed rather than preserved as a redirect, alias, or hidden fallback reader.
- **FR-018**: The legacy operations routes `/admin/operations` and `/admin/operations/{run}` MUST be removed in favor of `/admin/workspaces/{workspace}/operations` and `/admin/workspaces/{workspace}/operations/{run}` with no redirects, aliases, or dual-route ownership.
### Authorization and Safety Requirements
- **AR-001**: Workspace membership MUST remain the first access boundary for `/admin/workspaces/{workspace}/...` routes.
- **AR-002**: Managed-environment access MUST remain the second access boundary for `/admin/workspaces/{workspace}/environments/{environment}/...` routes.
- **AR-003**: A mismatched `{workspace}` and `{environment}` pair MUST resolve as `404` even if the actor has access to one of them individually.
- **AR-004**: Workspace-canonical pages that accept explicit environment filters MUST reject inaccessible environment hints as `404` and must not silently widen scope.
- **AR-005**: Any destructive action touched while retargeting dashboard or page links MUST remain server-authorized and require confirmation.
### Non-Functional Requirements
- **NFR-001**: Filament remains v5 on Livewire v4.
- **NFR-002**: Provider registration remains in `apps/platform/bootstrap/providers.php`; if `TenantPanelProvider` is removed from operator routing, no registration is moved to `bootstrap/app.php`.
- **NFR-003**: Existing admin theme and asset registration remain unchanged; the slice introduces no new asset pipeline or deployment step beyond the current Filament asset practice.
- **NFR-004**: The cutover must preserve Action Surface Contract, UI-FIL-001, and UX-001 expectations for touched Filament pages/resources without introducing ad hoc Blade/Tailwind design systems or redundant inspect actions.
- **NFR-005**: The route cutover must stay reviewable as one bounded slice and must not silently absorb provider extraction, artifact retargeting, RBAC redesign, copy neutralization, or quality-gate work reserved for later specs.
## UI Action Matrix *(mandatory when Filament is changed)*
| 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 dashboard | `WorkspaceOverview` under `/admin/workspaces/{workspace}` | none | body CTA cards and quick actions only | none | none | existing workspace zero-state CTA family only | none | `N/A` | no new audit behavior | Reuses `WorkspaceOverviewBuilder`; action-surface exemptions remain intact |
| Workspace-scoped environment chooser | chooser surface under `/admin/workspaces/{workspace}/environments` | none | environment card/button opens the environment dashboard | none | none | workspace switch or no-access guidance only | none | `N/A` | no new audit behavior required by this slice | Reuses the existing chooser pattern; selection must not jump into a second panel |
| Managed-environment dashboard | `TenantDashboard` under `/admin/workspaces/{workspace}/environments/{environment}` | `Primary follow-up`, `More`, `Request support`, `Open support diagnostics` | `N/A` | none | none | existing dashboard empty-state behavior only | same as header actions | `N/A` | existing support-request audit behavior only | Action hierarchy and support flows stay intact; route and breadcrumb ownership change only |
| Workspace operations hub | `Monitoring\Operations` under `/admin/workspaces/{workspace}/operations` | `Scope operations`, `Back to ...`, `Show all environments` | clickable row to run detail | none | none | existing operations empty-state only | existing run-viewer contract only | `N/A` | no new audit behavior | Filter and back-link context replace environment-panel routing; no new bulk or destructive actions |
All other environment-scoped resources and diagnostic pages that today live under `panel: 'tenant'` or `/admin/tenants/{environment}` keep their current local action contracts. This slice changes their route ownership and breadcrumb shell only and must not introduce new redundant View actions, placeholder action groups, or new destructive actions.
### Key Entities *(include if feature involves data)*
- **Workspace**: the operator's primary admin context and the only Filament tenant after this cutover; owns the canonical workspace dashboard, environment chooser, and workspace operations hub.
- **Managed Environment**: the environment-scoped managed target inside one workspace; owns the canonical environment dashboard route and remains the nested context for environment-scoped pages and explicit environment filters.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: All touched operator entry and drillthrough flows open workspace-first environment URLs, and no operator-visible `/admin/t/` link remains on touched admin surfaces.
- **SC-002**: An operator can go from workspace selection to one managed-environment dashboard and then into the workspace operations hub with preserved scope in three interactions or fewer.
- **SC-003**: All touched environment-scoped pages present breadcrumb/context order `Workspace -> Managed Environment -> page`, and direct wrong-workspace or wrong-environment access reveals no data.
- **SC-004**: Touched globally searchable resources either open a valid destination on first attempt under the new route family or no longer appear in global search.