## Summary - retire legacy `/admin/t` and active `/admin/tenants` product surfaces in favor of canonical workspace-scoped managed-environment routes - centralize runtime URL generation through `ManagedEnvironmentLinks` and update intended URL handling to reject legacy tenant paths - remove dormant tenant panel runtime, rename test helpers to the admin environment context, and add guard coverage for route/helper regressions ## Validation - targeted Feature guard, workspace, provider connection, required permissions, and Filament test lanes run under Sail - browser smoke coverage run for provider connection and workspace RBAC environment access flows - formatting and diff checks completed with Pint and `git diff --check` ## Notes - Filament remains on v5 with Livewire v4 - provider registration stays in `apps/platform/bootstrap/providers.php` - retired tenant resource global search is disabled and destructive action confirmation rules remain unchanged Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #352
20 KiB
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
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 to297-managed-environment-canonical-route-cutover. - Working tree was clean before creating the spec package.
TenantPanelProviderstill exists atapps/platform/app/Providers/Filament/TenantPanelProvider.php.apps/platform/bootstrap/providers.phpis already guarded by existing tests against registeringTenantPanelProvider.route:list --path=admin/tenantscurrently shows four active Filament tenant resource routes: index, view, edit, memberships.route:list --path=admin/workspacescurrently shows canonical environment routes under/admin/workspaces/{workspace}/environments...and workspace operations under/admin/workspaces/{workspace}/operations....rgcurrently finds many active tests/runtime references toTenantResource::getUrl(...),TenantDashboard::getUrl(...),TenantRequiredPermissions::getUrl(...),/admin/t/...,/admin/tenants..., andsetTenantPanelContext().- 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
Tenantproduct copy in touched files. - Special surface test profiles:
route-contract,standard-native-filament,global-context-shell,browser-smokeif 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
Tenantreferences must be listed inlegacy-surface-audit.mdor 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
ManagedEnvironmentLinkshelper 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
Tenantmodel 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:
OperationRunLinksandadmin.operations.index/admin.operations.viewwith explicit workspace context. - Delegated UX behaviors: preserve existing
View operation/Open operationbehavior. - 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.TenantPanelProvidermust not be registered there. - Globally searchable resources: If
TenantResourceis 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:
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)
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:
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
- Run:
git status --short --branch
git diff --stat
git log -1 --oneline
- Confirm the implementation branch is
297-managed-environment-canonical-route-cutoveror a session branch created from it. - Stop if unrelated uncommitted changes exist.
- Read:
.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:
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.phpif no true runtime dependency exists. - Ensure
apps/platform/bootstrap/providers.phpdoes 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
ManagedEnvironmentLinksonly 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/tenantsor/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/operationsto 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 examplesetAdminEnvironmentContext(). - 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:
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:
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.