# Implementation Plan: Managed Environment Canonical Route Cutover & Legacy Tenant Surface Retirement

**Branch**: `297-managed-environment-canonical-route-cutover` | **Date**: 2026-05-12 | **Spec**: [spec.md](/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/297-managed-environment-canonical-route-cutover/spec.md)  
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/297-managed-environment-canonical-route-cutover/spec.md`

## Summary

Spec 297 completes the hard product cutover from legacy tenant surfaces to canonical workspace-managed-environment routes. The implementation retires active `/admin/tenants...` product routes, keeps `/admin/t...` dead, removes or permanently neutralizes `TenantPanelProvider`, replaces runtime link generation with one canonical managed-environment link contract, rejects legacy intended URLs, renames the old tenant-panel test helper with no alias, and adds guard tests that prevent backsliding.

This plan is preparation only. It does not implement application code.

## Technical Context

**Language/Version**: PHP 8.4.15  
**Primary Dependencies**: Laravel 12.52.0, Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, Laravel Sail 1.52.0  
**Storage**: PostgreSQL through Laravel/Sail for tests; no new storage planned  
**Testing**: Pest via `./vendor/bin/sail artisan test --compact`; Browser tests only if visible navigation is touched  
**Validation Lanes**: targeted Feature guards, Workspaces, ProviderConnections, RequiredPermissions, Filament, Spec 288 guard pack, Spec 293 cutover lane, optional Browser smoke  
**Target Platform**: Laravel Sail local runtime and Gitea-compatible CI runners  
**Project Type**: Laravel web application under `apps/platform`  
**Performance Goals**: Route/link guards stay deterministic and focused; no new heavy or browser defaults  
**Constraints**: no `/admin/t...` restoration, no `/admin/tenants...` compatibility surface, no TenantPanelProvider reactivation, no old helper alias, no DB/model rename, no broad localization or RBAC refactor  
**Scale/Scope**: Route, link, intended URL, Filament resource registration, and test-helper cutover only

## Initial Repo Baseline

Preparation audit on 2026-05-12 found:

- Current branch before Spec Kit execution: `platform-dev`; Spec Kit switched to `297-managed-environment-canonical-route-cutover`.
- Working tree was clean before creating the spec package.
- `TenantPanelProvider` still exists at `apps/platform/app/Providers/Filament/TenantPanelProvider.php`.
- `apps/platform/bootstrap/providers.php` is already guarded by existing tests against registering `TenantPanelProvider`.
- `route:list --path=admin/tenants` currently shows four active Filament tenant resource routes: index, view, edit, memberships.
- `route:list --path=admin/workspaces` currently shows canonical environment routes under `/admin/workspaces/{workspace}/environments...` and workspace operations under `/admin/workspaces/{workspace}/operations...`.
- `rg` currently finds many active tests/runtime references to `TenantResource::getUrl(...)`, `TenantDashboard::getUrl(...)`, `TenantRequiredPermissions::getUrl(...)`, `/admin/t/...`, `/admin/tenants...`, and `setTenantPanelContext()`.
- The attempted `route:list --columns=...` option is unsupported in this Laravel version; retry without `--columns`.

The implementation must refresh `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/297-managed-environment-canonical-route-cutover/legacy-surface-audit.md` before editing runtime code.

## UI / Surface Guardrail Plan

- **Guardrail scope**: changed route/link/navigation contract for existing surfaces; no new product workflow.
- **Native vs custom classification summary**: Native Filament/resources/pages and shared link helpers. No custom Blade/Tailwind surface is planned.
- **Shared-family relevance**: navigation entry points, action links, notifications/toast actions, OperationRun links, provider/permission links, test context helpers.
- **State layers in scope**: route registration, URL helper, intended URL/session, Filament panel/resource registration, test panel/workspace/environment context.
- **Audience modes in scope**: operator-MSP and support-platform only through existing surfaces.
- **Decision/diagnostic/raw hierarchy plan**: existing environment/readiness/operations surfaces keep their hierarchy; the cutover only changes canonical route truth.
- **Raw/support gating plan**: unchanged; raw provider detail remains where existing policies allow it.
- **One-primary-action / duplicate-truth control**: do not add parallel actions to preserve legacy paths. Replace old destinations with canonical ones.
- **Handling modes by drift class or surface**: retire, replace, or document allowed technical reference. Unsafe or ambiguous legacy URL resolution falls back or 404s.
- **Repository-signal treatment**: review-mandatory for route-list output, source-scan allowlists, intended URL fallback, helper rename, and any remaining `Tenant` product copy in touched files.
- **Special surface test profiles**: `route-contract`, `standard-native-filament`, `global-context-shell`, `browser-smoke` if visible navigation changes.
- **Required tests or manual smoke**: targeted Pest guard tests first; Browser only when implementation touches visible navigation flows.
- **Exception path and spread control**: Allowed remaining technical `Tenant` references must be listed in `legacy-surface-audit.md` or final summary.
- **Active feature PR close-out entry**: Guardrail / Route Cutover / Smoke Coverage if browser proof was run.

## Shared Pattern & System Fit

- **Cross-cutting feature marker**: yes.
- **Systems touched**: Filament panel providers, TenantResource/TenantDashboard/TenantRequiredPermissions routes or links, WorkspaceRedirectResolver/intended URL support, OperationRunLinks, WorkspaceOverviewBuilder, provider/required-permissions link emitters, `tests/Pest.php`, guard tests, browser tests when route navigation is visible.
- **Shared abstractions reused**: existing workspace/environment routes, `WorkspaceContext`, `OperationRunLinks`, existing admin panel context helper, existing Spec 288/293 guard style.
- **New abstraction introduced? why?**: Only a bounded `ManagedEnvironmentLinks` helper if no existing repo-real helper owns canonical environment URLs. It exists to remove scattered route-name literals and prevent legacy URL generation.
- **Why the existing abstraction was sufficient or insufficient**: The canonical routes exist, but runtime link generation remains scattered and some helpers still emit old destinations.
- **Bounded deviation / spread control**: Technical `Tenant` model names and Microsoft tenant ID copy remain only where non-product or provider-specific.

## OperationRun UX Impact

- **Touches OperationRun start/completion/link UX?**: yes, route/link safety only.
- **Central contract reused**: `OperationRunLinks` and `admin.operations.index` / `admin.operations.view` with explicit workspace context.
- **Delegated UX behaviors**: preserve existing `View operation` / `Open operation` behavior.
- **Surface-owned behavior kept local**: environment/provider surfaces own only initiation inputs and page-local copy.
- **Queued DB-notification policy**: N/A.
- **Terminal notification path**: unchanged.
- **Exception path**: none.

## Provider Boundary & Portability Fit

- **Shared provider/platform boundary touched?**: yes.
- **Provider-owned seams**: Microsoft Entra tenant ID copy, Graph permission names, provider diagnostics payloads.
- **Platform-core seams**: route family, link generation, workspace/environment context, operations links, RBAC and access-scope semantics.
- **Neutral platform terms / contracts preserved**: workspace, managed environment, provider connection, target scope, required permissions, diagnostics, access scope, operation.
- **Retained provider-specific semantics and why**: Microsoft-specific identity/permission terms remain only when they identify external provider truth.
- **Bounded extraction or follow-up path**: No multi-provider framework. Follow-up only for DB/model rename or broader provider-boundary cleanup beyond route cutover.

## Constitution Check

*GATE: Must pass before runtime implementation and re-check before close-out.*

- Inventory-first: no new inventory or snapshot truth.
- Read/write separation: no new write workflow. Existing destructive actions touched by route/resource work keep confirmation, authorization, and audit behavior.
- Single Graph contract path: no new Graph calls.
- Deterministic capabilities: capability-first RBAC remains authoritative; no role-string checks.
- Proportionality / no premature abstraction: use existing helper if possible; any new link helper is bounded to current route generation.
- No new persisted truth: no migrations, tables, compatibility shims, or dual-read paths.
- Workspace isolation: all environment and operations links carry explicit workspace context or validate current workspace context.
- Tenant isolation: tenant-owned records exposed through canonical environment routes still enforce managed-environment entitlement.
- RBAC-UX: non-member/out-of-scope remains 404; established member missing capability remains 403; UI hiding is not security.
- Provider boundary: tenant-first platform route language is retired; provider-specific tenant terms remain only provider-owned.
- Test governance: guard tests are allowed and focused; no full-suite repair or new lane framework.
- Filament-native UI: Filament remains v5 on Livewire v4, no v3/v4 API usage, no ad-hoc UI redesign.
- Deployment/ops: no asset registration is planned. If assets are unexpectedly registered, deploy notes include `cd apps/platform && php artisan filament:assets`.

## Filament v5 Output Contract

- **Livewire compliance**: Filament v5 targets Livewire v4.0+; current app has Livewire 4.1.4.
- **Provider registration location**: Laravel 12 provider registration must remain in `apps/platform/bootstrap/providers.php`. `TenantPanelProvider` must not be registered there.
- **Globally searchable resources**: If `TenantResource` is retired or moved out of active discovery, global search must be disabled for it or it must no longer register. Any managed-environment resource that remains globally searchable must have Edit or View pages.
- **Destructive actions**: This spec does not add destructive actions. Any touched existing destructive action must still execute through `->action(...)`, use `->requiresConfirmation()`, and enforce server-side authorization.
- **Asset strategy**: No new Filament assets are planned. If implementation unexpectedly registers assets, deployment must include `cd apps/platform && php artisan filament:assets`.
- **Testing plan**: Pages/actions/helpers changed by the cutover are covered with Pest/Filament tests; guard tests cover route resurrection, helper resurrection, intended URL rejection, legacy URL generation, and managed-environment canonical links.

## Test Governance Check

- **Test purpose / classification by changed surface**: Feature guard tests for route/link/intended URL contracts; Unit tests for pure helper logic; Feature/Filament tests for pages/resources; Browser only for visible navigation smoke.
- **Affected validation lanes**: Feature/Guards, Feature/Workspaces, Feature/ProviderConnections, Feature/RequiredPermissions, Feature/Filament, Spec 288 guard pack, Spec 293 cutover lane, optional Browser lane.
- **Why this lane mix is the narrowest sufficient proof**: The risk is route/link resurrection, not complete product behavior. Focused guards plus existing domain test directories prove the changed contracts.
- **Narrowest proving commands**:

```bash
cd apps/platform
./vendor/bin/sail artisan test --compact tests/Feature/Guards/NoLegacyTenantPanelRuntimeTest.php
./vendor/bin/sail artisan test --compact tests/Feature/Guards/NoActiveTenantResourceRoutesTest.php
./vendor/bin/sail artisan test --compact tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php
./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php
```

- **Fixture / helper / factory / seed / context cost risks**: The replacement for `setTenantPanelContext()` must not make provider setup, browser fixtures, or broad workspace setup implicit.
- **Expensive defaults or shared helper growth introduced?**: none planned.
- **Heavy-family additions, promotions, or visibility changes**: none planned.
- **Surface-class relief / special coverage rule**: Standard-native Filament coverage unless route/navigation changes are visible in browser flows.
- **Closing validation and reviewer handoff**: run focused guards, affected domain directories, Spec 288 pack, Spec 293 pack, and Pint dirty.
- **Budget / baseline / trend follow-up**: document any material guard runtime increase in the implementation close-out.
- **Review-stop questions**: Does `/admin/tenants...` still return a product page? Does a helper still emit legacy URLs? Does intended URL handling preserve legacy paths? Did a test helper alias keep the old name? Did RBAC weaken?
- **Escalation path**: document-in-feature for allowed technical references; follow-up-spec for structural rename/localization issues.
- **Active feature PR close-out entry**: Guardrail / Route Cutover.
- **Why no dedicated follow-up spec is needed**: The route cutover is bounded. DB/model rename and broader copy/localization are explicit non-goals and can become follow-ups only if product needs them.

## Project Structure

### Documentation (this feature)

```text
specs/297-managed-environment-canonical-route-cutover/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── legacy-surface-audit.md
├── tasks.md
├── contracts/
│   └── managed-environment-canonical-route-contract.md
└── checklists/
    └── requirements.md
```

### Source Code (repository root)

Expected touched surfaces during implementation:

```text
apps/platform/app/
├── Providers/Filament/
├── Filament/
│   ├── Pages/
│   └── Resources/
├── Support/
│   ├── Workspaces/
│   ├── OperationRunLinks.php
│   └── ManagedEnvironmentLinks.php (only if needed)
└── Http/

apps/platform/bootstrap/providers.php
apps/platform/routes/web.php
apps/platform/tests/
├── Pest.php
├── Feature/
├── Unit/
└── Browser/
```

**Structure Decision**: Use existing Laravel/Filament app structure and existing route/helper/test conventions. Do not create a new base application folder or dependency.

## Complexity Tracking

| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| Cross-cutting route/link/test-helper cleanup | Legacy route truth exists in multiple owners and cannot be retired safely in one local page | Local cleanup would leave intended URLs, tests, or link builders able to resurrect old paths |
| Bounded canonical link helper if needed | Runtime link generation must have one owner to make guard tests meaningful | Scattered route-name literals would recreate the drift this spec removes |
| New guard tests | Regression risk is route/link resurrection after a cutover | Manual review and ad hoc source scans are not durable enough |

## Phase 0: Safety Gate

1. Run:

```bash
git status --short --branch
git diff --stat
git log -1 --oneline
```

2. Confirm the implementation branch is `297-managed-environment-canonical-route-cutover` or a session branch created from it.
3. Stop if unrelated uncommitted changes exist.
4. Read:

```text
.specify/memory/constitution.md
specs/287-cutover-prerequisite-completion/
specs/288-quality-gates-no-legacy-enforcement/
specs/293-post-cutover-suite-stabilization/
specs/296-full-suite-green-signal-restoration/
```

## Phase 1: Baseline Audit

Refresh `legacy-surface-audit.md` before code edits:

```bash
git status --short --branch
git diff --stat

cd apps/platform
./vendor/bin/sail artisan route:list | rg "admin/t|admin/tenants|provider-connections|required-permissions|workspaces/.*/environments|operations"
rg "TenantPanelProvider|panel:\s*'tenant'|panel:\s*\"tenant\"|/admin/t/|/admin/tenants|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext|admin\.operations" . --glob '!vendor' --glob '!node_modules'
```

Classify each finding as runtime, test, copy, historical, provider-specific, or allowed technical reference.

## Phase 2: Remove Dormant TenantPanelProvider

- Delete `apps/platform/app/Providers/Filament/TenantPanelProvider.php` if no true runtime dependency exists.
- Ensure `apps/platform/bootstrap/providers.php` does not reference it.
- Replace tests that inspect the file with provider-registration and route-list guards.
- Add/extend `NoLegacyTenantPanelRuntimeTest`.

## Phase 3: Establish Canonical Managed Environment Link Contract

- Locate repo-real managed-environment route helpers first.
- Create or extend `ManagedEnvironmentLinks` only if needed.
- Cover index/detail/required-permissions/diagnostics/access-scopes/operations.
- Replace direct legacy link generation in runtime surfaces.
- Add contract tests that assert no generated URL contains `/admin/tenants` or `/admin/t/`.

## Phase 4: Retire `/admin/tenants...`

- Remove active TenantResource route registration or move it out of active discovery.
- If a temporary redirect is unavoidable, require unique workspace/environment resolution and document the exception. Default is 404.
- Update global search for any retired resource.
- Add/extend `NoActiveTenantResourceRoutesTest`.

## Phase 5: Intended URL Legacy Rejection

- Update `WorkspaceRedirectResolver`, `WorkspaceIntendedUrl`, or repo-real intended URL owners.
- Reject `/admin/t...` and `/admin/tenants...` as final destinations.
- Normalize legacy `/admin/operations` to workspace operations when workspace is known.
- Fall back to workspace home or environment index when unsafe.
- Keep external URLs blocked.

## Phase 6: Required Permissions And Provider Connections

- Replace old required-permissions and provider-connection tenant URLs.
- Ensure tenantless provider-connection resource remains canonical.
- Ensure required-permissions uses the workspace/environment route.
- Add/extend legacy route tests proving old URLs do not return 200.

## Phase 7: Test Helper Rename

- Rename `setTenantPanelContext()` to the chosen canonical helper, for example `setAdminEnvironmentContext()`.
- Update every test usage.
- Do not keep an alias under the old name.
- Add guard coverage that fails on old helper resurrection.

## Phase 8: Copy Cleanup In Touched Active Surfaces

- Replace tenant-first product copy only in files touched by this cutover.
- Keep Microsoft/provider-specific tenant ID copy where correct.
- List remaining old references in `legacy-surface-audit.md`.

## Phase 9: Regression Proof Pack

Run focused proof:

```bash
cd apps/platform
./vendor/bin/sail artisan test --compact \
  tests/Feature/Guards/NoLegacyTenantPanelRuntimeTest.php \
  tests/Feature/Guards/NoActiveTenantResourceRoutesTest.php \
  tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php \
  tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php \
  tests/Feature/ProviderConnections/LegacyRedirectTest.php \
  tests/Feature/ManagedEnvironment/LegacyTenantCoreGuardTest.php \
  tests/Feature/Spec080WorkspaceManagedTenantAdminMigrationTest.php \
  tests/Feature/Filament/ManagedEnvironmentAccessScopeManagementTest.php \
  tests/Feature/Rbac/ProviderConnectionWorkspaceFirstPolicyTest.php
```

Run the Spec 288 guard pack and Spec 293 cutover lane listed in the spec. Run browser smoke only if visible navigation flows were touched.

## Phase 10: Broad Validation

Run at least:

```bash
cd apps/platform
./vendor/bin/sail artisan test --compact tests/Feature/Guards
./vendor/bin/sail artisan test --compact tests/Feature/Workspaces
./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections
./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions
./vendor/bin/sail artisan test --compact tests/Feature/Filament
./vendor/bin/sail bin pint --dirty --format agent
git diff --check
```

Raw full suite is optional unless requested; if run, record the exact result.
