From b98bafcf86fa08fe813eef87a2fac23e59d1a61c Mon Sep 17 00:00:00 2001 From: ahmido Date: Wed, 13 May 2026 20:33:30 +0000 Subject: [PATCH] feat: finalize managed environment cutover seal (#354) ## Summary - replace the remaining workspace overview tenant-first copy with environment-first wording in the builder, Blade view, and focused feature assertions - add the Spec 299 workspace overview browser smoke and the final cutover audit documenting fixed copy, clean runtime scans, and allowed internal/provider/regression-guard `Tenant` references - add the Spec 299 spec package (`spec.md`, `plan.md`, `tasks.md`, checklist, audit) to close the managed-environment cutover with an explicit final seal decision ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceOverviewContentTest.php tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php` - `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/Filament/PanelNavigationSegregationTest.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` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `git diff --check` ## Notes - Filament remains on Livewire v4. - Provider registration remains in `apps/platform/bootstrap/providers.php`. - No new panel provider or asset-strategy changes are included. - Remaining technical `Tenant` references are documented in `specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md`. Co-authored-by: Ahmed Darrazi Reviewed-on: https://git.cloudarix.de/ahmido/TenantAtlas/pulls/354 --- .../Workspaces/WorkspaceOverviewBuilder.php | 18 +- .../pages/workspace-overview.blade.php | 6 +- ...9WorkspaceOverviewCutoverSealSmokeTest.php | 42 +++ ...oChooseTenantWhenWorkspaceSelectedTest.php | 3 +- .../Filament/WorkspaceOverviewContentTest.php | 9 +- .../WorkspaceOverviewEmptyStatesTest.php | 6 +- .../checklists/requirements.md | 69 ++++ .../final-cutover-audit.md | 128 +++++++ .../plan.md | 350 +++++++++++++++++ .../spec.md | 351 ++++++++++++++++++ .../tasks.md | 134 +++++++ 11 files changed, 1098 insertions(+), 18 deletions(-) create mode 100644 apps/platform/tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php create mode 100644 specs/299-managed-environment-cutover-final-seal/checklists/requirements.md create mode 100644 specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md create mode 100644 specs/299-managed-environment-cutover-final-seal/plan.md create mode 100644 specs/299-managed-environment-cutover-final-seal/spec.md create mode 100644 specs/299-managed-environment-cutover-final-seal/tasks.md diff --git a/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php b/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php index e19258a7..29dfaffd 100644 --- a/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php +++ b/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php @@ -257,7 +257,7 @@ private function myFindingsSignal(int $workspaceId, array $visibleTenantIds, Use Str::plural('finding', $openAssignedCount), ), 'description' => $isCalm - ? 'No visible assigned findings currently need attention across your entitled tenants.' + ? 'No visible assigned findings currently need attention across your entitled environments.' : sprintf( 'Visible assigned work stays in one queue. %d overdue %s currently need follow-up.', $overdueAssignedCount, @@ -317,7 +317,7 @@ private function visibleFindingTenantIds(Collection $accessibleTenants, User $us private function findingsHygieneDescription(int $brokenAssignmentCount, int $staleInProgressCount): string { if ($brokenAssignmentCount === 0 && $staleInProgressCount === 0) { - return 'No broken assignments or stale in-progress work are visible across your entitled tenants.'; + return 'No broken assignments or stale in-progress work are visible across your entitled environments.'; } if ($brokenAssignmentCount > 0 && $staleInProgressCount > 0) { @@ -557,7 +557,7 @@ private function attentionItems( badge: 'Governance', badgeColor: 'danger', destination: $this->tenantDashboardTarget($tenant, $user, 'Open environment'), - supportingMessage: 'Open the tenant dashboard to review the full invalid-governance family without narrowing the findings set.', + supportingMessage: 'Open the environment dashboard to review the full invalid-governance family without narrowing the findings set.', )]; } @@ -721,7 +721,7 @@ private function attentionItems( urgency: 'supporting', title: 'Operations are running', body: sprintf( - '%d run%s are currently queued or in progress for this tenant.', + '%d run%s are currently queued or in progress for this environment.', $activeOperationsCount, $activeOperationsCount === 1 ? '' : 's', ), @@ -741,7 +741,7 @@ private function attentionItems( urgency: 'supporting', title: 'Alert deliveries failed', body: sprintf( - '%d alert delivery attempt%s failed in the last 7 days for this tenant.', + '%d alert delivery attempt%s failed in the last 7 days for this environment.', $alertFailuresCount, $alertFailuresCount === 1 ? '' : 's', ), @@ -988,7 +988,7 @@ private function summaryMetrics( $metrics = [ $this->makeSummaryMetric( key: 'accessible_tenants', - label: 'Accessible tenants', + label: 'Accessible environments', value: $accessibleTenantCount, category: 'scope', description: $accessibleTenantCount > 0 @@ -1004,7 +1004,7 @@ private function summaryMetrics( label: 'Governance attention', value: $governanceAttentionTenantCount, category: 'governance_risk', - description: 'Affected visible tenants with overdue findings, governance expiry, lapsed governance, or compare posture that needs review.', + description: 'Affected visible environments with overdue findings, governance expiry, lapsed governance, or compare posture that needs review.', color: $governanceAttentionTenantCount > 0 ? 'danger' : 'gray', destination: $governanceAttentionTenantCount > 0 ? $this->chooseTenantTarget(__('localization.shell.choose_environment')) @@ -1368,8 +1368,8 @@ private function calmnessState( return [ 'is_calm' => false, 'checked_domains' => $checkedDomains, - 'title' => 'No accessible tenants in this workspace', - 'body' => 'This workspace is not calm or healthy yet because your current scope has no visible tenants. Switch workspace or review workspace-wide operations while access is being restored.', + 'title' => 'No accessible environments in this workspace', + 'body' => 'This workspace is not calm or healthy yet because your current scope has no visible environments. Switch workspace or review workspace-wide operations while access is being restored.', 'next_action' => $this->switchWorkspaceTarget(), ]; } diff --git a/apps/platform/resources/views/filament/pages/workspace-overview.blade.php b/apps/platform/resources/views/filament/pages/workspace-overview.blade.php index dddae8ef..c6810359 100644 --- a/apps/platform/resources/views/filament/pages/workspace-overview.blade.php +++ b/apps/platform/resources/views/filament/pages/workspace-overview.blade.php @@ -28,7 +28,7 @@

- This home stays workspace-scoped even when you were previously working in a tenant. Governance risk is still ranked ahead of execution noise, backup health stays separate from recovery evidence, and calm wording only appears when visible tenants are genuinely quiet across the checked domains. + This home stays workspace-scoped even when you were previously working in an environment. Governance risk is still ranked ahead of execution noise, backup health stays separate from recovery evidence, and calm wording only appears when visible environments are genuinely quiet across the checked domains.

@@ -175,13 +175,13 @@ class="rounded-xl border border-gray-200 bg-white px-4 py-3 text-left transition
- Governance risk counts affected tenants + Governance risk counts affected environments Backup health stays separate from recovery evidence - Calm wording stays bounded to visible tenants and checked domains + Calm wording stays bounded to visible environments and checked domains Recent operations stay diagnostic diff --git a/apps/platform/tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php b/apps/platform/tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php new file mode 100644 index 00000000..0368d31c --- /dev/null +++ b/apps/platform/tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php @@ -0,0 +1,42 @@ +browser()->timeout(20_000); + +it('smokes workspace overview environment-first final cutover copy', function (): void { + $user = User::factory()->create(); + $workspace = Workspace::factory()->create([ + 'name' => 'Spec 299 Workspace', + ]); + + WorkspaceMembership::factory()->create([ + 'workspace_id' => (int) $workspace->getKey(), + 'user_id' => (int) $user->getKey(), + 'role' => 'owner', + ]); + + $this->actingAs($user)->withSession([ + WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), + ]); + session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); + + visit(route('admin.workspace.home', ['workspace' => $workspace])) + ->waitForText('Workspace overview') + ->assertSee('No accessible environments in this workspace') + ->assertSee('Governance risk counts affected environments') + ->assertSee('Calm wording stays bounded to visible environments and checked domains') + ->assertDontSee('No accessible tenants in this workspace') + ->assertDontSee('Governance risk counts affected tenants') + ->assertDontSee('Calm wording stays bounded to visible tenants and checked domains') + ->assertNoJavaScriptErrors() + ->assertNoConsoleLogs(); +}); diff --git a/apps/platform/tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php b/apps/platform/tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php index ef8cec00..37570998 100644 --- a/apps/platform/tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php +++ b/apps/platform/tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php @@ -29,7 +29,8 @@ ->get(route('admin.workspace.home', ['workspace' => $workspace])) ->assertOk() ->assertSee('Workspace overview') - ->assertSee('No accessible tenants in this workspace'); + ->assertSee('No accessible environments in this workspace') + ->assertDontSee('No accessible tenants in this workspace'); }); it('renders the workspace overview when a workspace is selected and has multiple tenants', function (): void { diff --git a/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php b/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php index 1998bfbe..93689601 100644 --- a/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php +++ b/apps/platform/tests/Feature/Filament/WorkspaceOverviewContentTest.php @@ -33,7 +33,7 @@ $response ->assertOk() ->assertSee('Workspace overview') - ->assertSee('Accessible tenants') + ->assertSee('Accessible environments') ->assertSee('Governance attention') ->assertSee('Backup attention') ->assertSee('Recovery attention') @@ -47,9 +47,12 @@ ->assertSee('Activity only. Active execution does not imply governance health.') ->assertSee('Visible environments with non-healthy backup posture.') ->assertSee('Visible environments with weakened or unvalidated recovery evidence.') - ->assertSee('Governance risk counts affected tenants') + ->assertSee('Governance risk counts affected environments') ->assertSee('Backup health stays separate from recovery evidence') - ->assertSee('Calm wording stays bounded to visible tenants and checked domains') + ->assertSee('Calm wording stays bounded to visible environments and checked domains') + ->assertDontSee('Accessible tenants') + ->assertDontSee('Governance risk counts affected tenants') + ->assertDontSee('Calm wording stays bounded to visible tenants and checked domains') ->assertSee('Inventory sync'); }); diff --git a/apps/platform/tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php b/apps/platform/tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php index 8abaad7f..35927809 100644 --- a/apps/platform/tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php +++ b/apps/platform/tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php @@ -28,8 +28,10 @@ ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get(route('admin.workspace.home', ['workspace' => $workspace])) ->assertOk() - ->assertSee('No accessible tenants in this workspace') - ->assertSee('This workspace is not calm or healthy yet because your current scope has no visible tenants.') + ->assertSee('No accessible environments in this workspace') + ->assertSee('This workspace is not calm or healthy yet because your current scope has no visible environments.') + ->assertDontSee('No accessible tenants in this workspace') + ->assertDontSee('This workspace is not calm or healthy yet because your current scope has no visible tenants.') ->assertSee('No recent operations yet') ->assertSee('Switch workspace') ->assertDontSee(__('localization.shell.choose_environment')); diff --git a/specs/299-managed-environment-cutover-final-seal/checklists/requirements.md b/specs/299-managed-environment-cutover-final-seal/checklists/requirements.md new file mode 100644 index 00000000..a7c97293 --- /dev/null +++ b/specs/299-managed-environment-cutover-final-seal/checklists/requirements.md @@ -0,0 +1,69 @@ +# Requirements Checklist: Managed Environment Cutover Final Seal & Regression Guard Pack + +**Purpose**: Validate that Spec 299 is bounded, implementation-ready, and constitution-aligned as the final managed-environment cutover seal. +**Created**: 2026-05-13 +**Feature**: [spec.md](/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/299-managed-environment-cutover-final-seal/spec.md) + +## Applicability And Final-Seal Gate + +- [x] CHK001 The change explicitly affects operator-facing navigation/copy plus route/helper/test/audit guardrails. +- [x] CHK002 The spec, plan, and tasks carry the same closure-only scope and do not reopen a new feature lane. +- [x] CHK003 The dependency/start-condition boundary is explicit: Spec 298 must be committed/merged or cleanly isolated before 299 runtime work begins. + +## Native, Shared-Family, And State Ownership + +- [x] CHK004 The scope keeps existing Laravel/Filament/Blade surfaces and does not introduce a new custom UI system. +- [x] CHK005 Route ownership, intended URL handling, navigation registration, workspace overview copy, and audit/allowlist tracking are named as separate state layers. +- [x] CHK006 Existing action hierarchy and inspect/open models remain unchanged unless a touched route or label needs a cutover fix. + +## Shared Pattern Reuse + +- [x] CHK007 The cross-cutting interaction classes are explicit: canonical links, intended URLs, navigation registration, workspace summary copy, guard tests, and browser anchors. +- [x] CHK008 The existing shared paths are named: `ManagedEnvironmentLinks`, `WorkspaceIntendedUrl`, `WorkspaceRedirectResolver`, `OperationRunLinks`, `setAdminEnvironmentContext()`, and current route-scope navigation owners. +- [x] CHK009 No parallel navigation, terminology, or cutover framework is introduced. + +## OperationRun Start UX Contract + +- [x] CHK010 The spec states that no new `OperationRun` start/completion behavior is introduced. +- [x] CHK011 Existing workspace operations link ownership remains shared and canonical if touched. +- [x] CHK012 No queued DB notification or terminal notification policy changes are planned. + +## Provider Boundary And Vocabulary + +- [x] CHK013 Provider-owned terms such as Microsoft tenant ID, Entra tenant ID, and target tenant scope are allowed only when the external provider is the subject. +- [x] CHK014 Generic platform/operator vocabulary is required to stay workspace/environment-first. + +## Signals, Exceptions, And Test Depth + +- [x] CHK015 Repository signals are classified as review-mandatory: final scans, navigation proof, helper retirement, browser selector changes, and allowlist decisions. +- [x] CHK016 Every remaining `Tenant` reference must be documented in `final-cutover-audit.md` with an explicit category. +- [x] CHK017 Required surface test profiles are explicit: `standard-native-filament`, `global-context-shell`, and targeted `browser-smoke` only when touched. +- [x] CHK018 The chosen test lanes are focused and do not require a raw full-suite repair loop. + +## Audience-Aware Disclosure And Decision Hierarchy + +- [x] CHK019 Workspace routes keep default-visible content workspace-first while environment drill-down stays explicit and secondary. +- [x] CHK020 Canonical environment routes remain the place where environment-owned navigation and diagnostics appear. +- [x] CHK021 The spec adds no new primary actions; it only seals current scope truth and touched copy. +- [x] CHK022 Duplicate or contradictory workspace/environment/tenant wording is treated as drift to fix or document. + +## Filament v5 Contract + +- [x] CHK023 Filament v5 / Livewire v4.0+ compliance is explicit. +- [x] CHK024 Laravel 12 panel provider registration remains in `apps/platform/bootstrap/providers.php`. +- [x] CHK025 Globally searchable resource handling is called out for any remaining technical `TenantResource` use. +- [x] CHK026 Destructive actions that are touched must retain `->action(...)`, `->requiresConfirmation()`, and authorization. +- [x] CHK027 Asset strategy is unchanged unless implementation unexpectedly registers assets, in which case `filament:assets` is required in deployment notes. +- [x] CHK028 The testing plan names affected guard tests, navigation tests, workspaces tests, provider/required-permissions lanes, Filament lane, and browser anchors. + +## Review Outcome + +- [x] CHK029 Review outcome class: `documentation-required-exception`. +- [x] CHK030 Workflow outcome: `keep`. +- [x] CHK031 Final note location: active feature PR close-out entry `Guardrail / Final Cutover Seal / Smoke Coverage`. + +## Notes + +- This is the final cutover seal package, not a new runtime feature spec. +- Remaining provider/internal/historical/guard-only `Tenant` references are allowed only when categorized in `final-cutover-audit.md`. +- The only explicit exception is the start-condition dependency on Spec 298 landing or clean isolation. \ No newline at end of file diff --git a/specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md b/specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md new file mode 100644 index 00000000..571deb5e --- /dev/null +++ b/specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md @@ -0,0 +1,128 @@ +# Final Cutover Audit: Managed Environment Cutover Final Seal & Regression Guard Pack + +**Feature**: `299-managed-environment-cutover-final-seal` +**Created**: 2026-05-13 +**Status**: Final implementation complete. Decision: `merge-ready with documented allowed internal Tenant references`. + +## Categories + +| Category | Meaning | +|---|---| +| `clean-baseline` | Prep-time scans found no active runtime legacy hit in the named surface. | +| `fixed` | The implementation replaced or removed the old cutover drift. | +| `allowed-provider-term` | The wording describes external Microsoft/Entra provider truth and may remain. | +| `allowed-internal-model` | The term is an internal model/class/table/column/resource name; structural rename is out of scope. | +| `allowed-historical` | The term appears in historical specs/docs/audit history that this spec does not rewrite. | +| `allowed-regression-guard` | The term is kept only as a forbidden legacy pattern or negative assertion inside a guard test. | +| `needs-follow-up` | The prep baseline found a real seam that 299 must classify or fix during implementation. | +| `blocked-runtime-finding` | The final implementation found an active runtime legacy seam that blocks merge. | + +## Preparation Baseline Evidence + +These entries reflect the repo evidence gathered during Spec 299 preparation before application code was edited. + +| Evidence | Result | Decision | +|---|---|---| +| Focused runtime legacy scan over `apps/platform/{app,resources,routes}/**` for `TenantPanelProvider`, retired tenant panel IDs, `/admin/t`, `/admin/tenants`, retired runtime URL generators, and `setTenantPanelContext` | No matches found in the runtime app/resources/routes tree. | Runtime baseline is clean; re-prove with route list and final scans during implementation. | +| File search for `apps/platform/**/TenantPanelProvider.php` | No file found. | Keep tenant panel provider absent. | +| Focused provider/copy scan for `tenant scope`, `Microsoft tenant`, `Entra tenant`, `Accessible tenants`, `No accessible tenants`, and `affected tenants` | 13 matches found across workspace overview, provider capability/identity wording, support diagnostics, onboarding technical details, and operations viewer copy. | Separate active product copy from allowed provider or support/raw wording. | +| Focused test scan for `TenantResource::getUrl`, `TenantDashboard::getUrl`, `TenantRequiredPermissions::getUrl`, `setTenantPanelContext`, `/admin/t/`, and `/admin/tenants` under `apps/platform/tests/**` | More than 100 matches found across auth, RBAC, workspaces, guards, provider-connections, and browser tests. | 299 must classify test references instead of trying to erase all technical legacy terms blindly. | +| Targeted file reads of canonical owners | `ManagedEnvironmentLinks`, `WorkspaceIntendedUrl`, `WorkspaceRedirectResolver`, `AdminPanelProvider`, `PanelNavigationSegregationTest`, and `WorkspaceOverviewBuilder` are the current repo-real owners. | Reuse these owners; do not invent a new cutover framework. | + +## Active Runtime Legacy Findings + +| Pattern | File / Surface | Category | Decision | Reason | +|---|---|---|---|---| +| Retired tenant runtime patterns: `TenantPanelProvider`, retired panel IDs, `/admin/t/...`, `/admin/tenants/...`, retired runtime URL generators, `setTenantPanelContext()` | `apps/platform/app/**`, `apps/platform/resources/**`, `apps/platform/routes/**` | clean-baseline | keep clean and revalidate | The prep runtime scan found no direct matches in the runtime tree. | +| Retired tenant panel provider file | `apps/platform/**/TenantPanelProvider.php` | clean-baseline | keep absent | File search returned no `TenantPanelProvider.php`. | +| Canonical environment link ownership | `apps/platform/app/Support/ManagedEnvironmentLinks.php` | clean-baseline | re-prove | The repo already has a canonical environment link helper; 299 must ensure no live seam bypasses it. | +| Retired intended-URL rejection and workspace-operations normalization | `apps/platform/app/Support/Workspaces/WorkspaceIntendedUrl.php`, `apps/platform/app/Support/Workspaces/WorkspaceRedirectResolver.php` | clean-baseline | re-prove | Current code already rejects retired tenant paths and normalizes workspace operations; the final seal must keep that contract explicit. | +| Workspace-vs-environment navigation ownership | `apps/platform/app/Providers/Filament/AdminPanelProvider.php`, affected `apps/platform/app/Filament/**`, `apps/platform/tests/Feature/Filament/PanelNavigationSegregationTest.php` | clean-baseline | proved | `PanelNavigationSegregationTest.php` passed as part of the focused proof pack and the full Filament lane; no navigation leak was reproduced. | + +## Test Legacy Findings + +| Pattern | File / Surface | Category | Decision | Reason | +|---|---|---|---|---| +| `setTenantPanelContext` forbidden-pattern literals | `apps/platform/tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php` | allowed-regression-guard | keep | These strings are allowed only as explicit guard literals preventing reintroduction of the retired helper. | +| Retired-path negative assertions such as `/admin/t/...` and `/admin/tenants/...` | `apps/platform/tests/Feature/078/**`, `apps/platform/tests/Feature/ProviderConnections/LegacyRedirectTest.php`, `apps/platform/tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php`, `apps/platform/tests/Browser/Spec282GovernanceArtifactRetargetingSmokeTest.php` | allowed-regression-guard | keep | These assertions are part of the negative regression contract and must remain explicit. | +| Broad `TenantDashboard::getUrl(...)` usage | Representative files include `apps/platform/tests/Feature/Auth/AdminLocalSmokeLoginTest.php`, `apps/platform/tests/Feature/Workspaces/ManagedTenantsWorkspaceRoutingTest.php`, `apps/platform/tests/Feature/Rbac/TenantDashboardArrivalContextVisibilityTest.php`, `apps/platform/tests/Feature/Filament/WorkspaceOverviewPermissionVisibilityTest.php` | allowed-internal-model | keep | These remain test references to the current technical page owner for canonical environment routes. Focused route scans and guard tests prove they do not revive `/admin/t...` or `/admin/tenants...`. | +| Broad `TenantResource::getUrl(...)` usage | Representative files include `apps/platform/tests/Feature/Rbac/TenantResourceAuthorizationTest.php`, `apps/platform/tests/Feature/RequiredPermissions/RequiredPermissionsLinksTest.php`, `apps/platform/tests/Feature/Filament/WorkspaceOnlySurfaceTenantIndependenceTest.php`, `apps/platform/tests/Browser/Spec192RecordPageHeaderDisciplineSmokeTest.php`, `apps/platform/tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php` | allowed-internal-model | keep | These remain test references to the current technical resource owner and route-generation contract. `ManagedEnvironmentCanonicalRouteContractTest.php` and the runtime scan prove generated URLs stay canonical. | + +## Copy Findings + +| Pattern | File | Category | Decision | Reason | +|---|---|---|---|---| +| `Accessible tenants` | `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` | fixed | replaced with `Accessible environments` | This active workspace-overview product copy is now environment-first and covered by Feature and Browser assertions. | +| `No accessible tenants in this workspace` | `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` | fixed | replaced with `No accessible environments in this workspace` | This active workspace empty-state title is now environment-first and covered by Feature and Browser assertions. | +| `Governance risk counts affected tenants` | `apps/platform/resources/views/filament/pages/workspace-overview.blade.php` | fixed | replaced with `Governance risk counts affected environments` | This active workspace-overview helper copy is now environment-first and covered by Feature and Browser assertions. | +| Other workspace-overview tenant-first helper copy (`entitled tenants`, `visible tenants`, `tenant dashboard`, `for this tenant`, `working in a tenant`) | `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php`, `apps/platform/resources/views/filament/pages/workspace-overview.blade.php` | fixed | replaced with environment-first wording | These strings were in the same touched active workspace-overview surface, so they were fixed in scope rather than merely classified. | +| `Provider connection is missing target tenant scope.`, `Microsoft tenant ID`, `The selected provider connection points to a different Microsoft tenant than the current scope.`, provider-owned Microsoft tenant detail helper text | `apps/platform/app/Services/Providers/PlatformProviderIdentityResolver.php`, `apps/platform/app/Support/Providers/TargetScope/ProviderIdentityContextMetadata.php`, `apps/platform/app/Support/Providers/ProviderReasonTranslator.php`, `apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php`, `apps/platform/app/Filament/Resources/ProviderConnectionResource.php` | allowed-provider-term | keep | These strings describe external provider truth and are not generic workspace/environment product labels. | +| `Provider capability could not be evaluated because the tenant scope is unavailable.`, `Workspace and tenant scope resolved before support diagnostics were composed.`, `The current workspace or tenant scope remains visible without behaving like a primary task action.` | `apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityEvaluator.php`, `apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php`, `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` | allowed-provider-term / allowed-internal-model | keep | These are provider capability, support diagnostics, or scope-debug explanatory strings, not primary workspace-overview product copy. | +| `Scope (Entra tenant)` | `apps/platform/resources/views/filament/modals/onboarding-verification-technical-details.blade.php` | allowed-provider-term | keep | This is provider-specific onboarding technical detail, not generic platform terminology. | + +## Allowed References + +| Reference Family | Example Files | Category | Why Allowed | +|---|---|---|---| +| Microsoft/Entra tenant identity and target-scope wording | `apps/platform/app/Support/Providers/TargetScope/ProviderIdentityContextMetadata.php`, `apps/platform/app/Filament/Resources/ProviderConnectionResource.php`, `apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php` | allowed-provider-term | The external provider is the subject. | +| Technical `Tenant*` model/class/resource names and DB columns | `apps/platform/app/**`, `apps/platform/tests/**` where structural owners remain | allowed-internal-model | DB/model/resource rename is out of scope for Spec 299. | +| Historical spec names and archived decisions | `specs/**`, `.specify/**`, historical docs | allowed-historical | 299 is a final seal package, not a history rewrite. | +| Explicit forbidden-pattern literals and negative-path assertions | `apps/platform/tests/Feature/Guards/**`, `apps/platform/tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php`, `apps/platform/tests/Feature/ProviderConnections/LegacyRedirectTest.php`, `apps/platform/tests/Browser/Spec282GovernanceArtifactRetargetingSmokeTest.php` | allowed-regression-guard | These references exist specifically to keep retired tenant behavior dead. | + +## Final Route / Source Scan Results + +Final implementation results must be recorded here before close-out. + +| Command | Result | Decision | +|---|---|---| +| `cd apps/platform && ./vendor/bin/sail artisan route:list | rg "admin/t|admin/tenants|workspaces/.*/environments|operations|provider-connections|required-permissions"` | Returned canonical `admin/workspaces/{workspace}/environments...`, provider-connections, required-permissions, and workspace operations routes; no active `/admin/t...` or `/admin/tenants...` route was returned. | clean | +| `cd apps/platform && rg "TenantPanelProvider|panel:\s*'tenant'|panel:\s*\"tenant\"|/admin/t/|/admin/tenants|filament\.admin\.resources\.tenants|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext" app resources routes --glob '!vendor' --glob '!node_modules'` | No matches. | clean | +| `cd apps/platform && rg "TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext|/admin/t/|/admin/tenants" tests --glob '!vendor' --glob '!node_modules'` | Matches remain in technical tests, browser smokes, and guard/negative-route assertions only. | allowed-internal-model / allowed-regression-guard | +| `cd apps/platform && rg "tenant scope|Microsoft tenant|Entra tenant|Accessible tenants|No accessible tenants|affected tenants" app resources lang tests --glob '!vendor' --glob '!node_modules'` | Active workspace-overview copy was fixed; old workspace-overview phrases now appear only in negative assertions. Provider/support/test scope terms remain allowlisted. | fixed + allowlisted | + +## Actual Validation Results + +| Command | Result | +|---|---| +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceOverviewContentTest.php tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php` | passed: 9 tests, 59 assertions | +| `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/Filament/PanelNavigationSegregationTest.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` | passed: 72 tests, 232 assertions | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards` | passed: 265 tests, 4,705 assertions | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces` | passed: 96 tests, 276 assertions | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections` | passed: 78 tests, 588 assertions | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions` | passed: 21 tests, 82 assertions | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament` | passed: 773 tests, 5 skipped, 5,017 assertions | +| `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php` | passed: 1 browser smoke test, 9 assertions | +| `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` | passed | +| `git diff --check` | passed | + +## Planned Validation Commands + +```bash +cd apps/platform + +./vendor/bin/sail artisan test --compact \ + tests/Feature/Guards/NoLegacyTenantPanelRuntimeTest.php \ + tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php \ + tests/Feature/Guards/NoActiveTenantResourceRoutesTest.php \ + tests/Feature/Filament/PanelNavigationSegregationTest.php \ + tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php + +./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 artisan test --compact \ + tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php \ + tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php \ + tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php \ + tests/Browser/Spec192RecordPageHeaderDisciplineSmokeTest.php + +./vendor/bin/sail bin pint --dirty --format agent +git diff --check +``` + +## Final Decision + +`merge-ready with documented allowed internal Tenant references` diff --git a/specs/299-managed-environment-cutover-final-seal/plan.md b/specs/299-managed-environment-cutover-final-seal/plan.md new file mode 100644 index 00000000..fb55f231 --- /dev/null +++ b/specs/299-managed-environment-cutover-final-seal/plan.md @@ -0,0 +1,350 @@ +# Implementation Plan: Managed Environment Cutover Final Seal & Regression Guard Pack + +**Branch**: `299-managed-environment-cutover-final-seal` | **Date**: 2026-05-13 | **Spec**: [spec.md](/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/299-managed-environment-cutover-final-seal/spec.md) +**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/299-managed-environment-cutover-final-seal/spec.md` + +## Summary + +Spec 299 is a final acceptance-and-regression package over the managed-environment cutover. The runtime app tree is already mostly aligned to the canonical owners introduced and hardened by Specs 297 and 298. The implementation therefore stays narrow: + +- re-run and document the final cutover baseline in `final-cutover-audit.md` +- verify the runtime tree remains clean of retired tenant routes and helper generators +- seal any remaining workspace-vs-environment navigation or intended-URL seam +- bound the last touched product-facing tenant-first copy on active surfaces +- classify remaining technical, provider-specific, historical, and guard-only `Tenant` references +- run the final focused proof pack and stop with one explicit cutover decision + +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 schema or persistence change planned +**Testing**: Pest via `./vendor/bin/sail artisan test --compact`; targeted Browser smoke only for touched visible anchors +**Validation Lanes**: Feature/Guards, Feature/Workspaces, Feature/ProviderConnections, Feature/RequiredPermissions, Feature/Filament, selected Browser anchors +**Target Platform**: Laravel Sail local runtime and Gitea-compatible CI runners +**Project Type**: Laravel web application under `apps/platform` +**Performance Goals**: keep the final proof bounded and reproducible; no raw full suite unless explicitly requested +**Constraints**: no compatibility routes, no DB/model rename, no broad localization sweep, no provider/RBAC rewrite, no new panel, no new asset strategy +**Scale/Scope**: closure of route/link/navigation/copy/test allowlist seams only + +## Initial Repo Baseline + +Preparation checks on 2026-05-13 found: + +- Current branch before Spec Kit execution was `node-dev`; Spec Kit created and checked out `299-managed-environment-cutover-final-seal`. +- The working tree was clean before creating the package. +- `docs/product/spec-candidates.md` currently says there is no safe automatic next-best-prep target. Spec 299 is therefore an explicit manual promotion directed by the user, not an auto-selected queue item. +- Related existing specs: + - `specs/297-managed-environment-canonical-route-cutover/` is dependency context and carries completed-task signals. + - `specs/298-managed-environment-terminology-copy-cleanup/` is dependency context and carries completed-task plus checklist review signals. + - Neither package may be rewritten back into preparation-only state. +- Current canonical owners are already repo-real: + - `apps/platform/app/Support/ManagedEnvironmentLinks.php` + - `apps/platform/app/Support/Workspaces/WorkspaceIntendedUrl.php` + - `apps/platform/app/Support/Workspaces/WorkspaceRedirectResolver.php` + - `apps/platform/app/Providers/Filament/AdminPanelProvider.php` + - `apps/platform/tests/Feature/Filament/PanelNavigationSegregationTest.php` + - `apps/platform/tests/Pest.php` with `setAdminEnvironmentContext()` +- A focused baseline scan found no direct retired tenant route/helper hits under `apps/platform/app`, `apps/platform/resources`, or `apps/platform/routes` for `TenantPanelProvider`, `/admin/t`, `/admin/tenants`, old runtime URL generators, or `setTenantPanelContext()`. +- The remaining confirmed pressure is in proof depth and residual wording: + - `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` still contains active workspace-overview labels such as `Accessible tenants` and `No accessible tenants in this workspace`. + - `apps/platform/resources/views/filament/pages/workspace-overview.blade.php` still renders `Governance risk counts affected tenants`. + - The test tree still contains many technical `TenantResource::getUrl(...)`, `TenantDashboard::getUrl(...)`, retired-path negative assertions, and explicit `setTenantPanelContext` forbidden-pattern guards that now need final classification instead of broad removal. + +Implementation-readiness note: + +- The spec package can be prepared now. +- Runtime implementation must still honor the start condition from the spec: 298 changes must be committed/merged or intentionally isolated before 299 runtime edits begin. + +## UI / Surface Guardrail Plan + +- **Guardrail scope**: changed workspace/environment navigation proof, workspace overview copy, and guardrail workflow evidence; no new product surface. +- **Native vs custom classification summary**: native Filament/Laravel/Blade surfaces plus shared route helpers. +- **Shared-family relevance**: navigation registration, context-bar/workspace summary copy, canonical links, intended URLs, and guard tests. +- **State layers in scope**: shell, page, detail, URL-query, test helper, and audit artifact. +- **Audience modes in scope**: operator-MSP and support-platform. +- **Decision/diagnostic/raw hierarchy plan**: keep workspace routes workspace-first and environment routes environment-first; do not surface extra raw/support detail. +- **Raw/support gating plan**: unchanged. +- **One-primary-action / duplicate-truth control**: no new actions; keep one scope truth per route and remove or document duplicate tenant-first wording. +- **Handling modes by drift class or surface**: active runtime seam = fix now; active product copy seam = fix now; technical/provider/historical reference = document in audit; broad rename request = out of scope. +- **Repository-signal treatment**: review-mandatory for final scans, navigation proof, helper retirement, browser anchor changes, and allowlist classification. +- **Special surface test profiles**: `standard-native-filament`, `global-context-shell`, `browser-smoke` when touched. +- **Required tests or manual smoke**: focused guards and feature tests first, browser anchors only for touched visible flows. +- **Exception path and spread control**: every surviving `Tenant` reference must be recorded in `final-cutover-audit.md` with reason and guard status. +- **Active feature PR close-out entry**: Guardrail / Final Cutover Seal / Smoke Coverage. + +## Shared Pattern & System Fit + +- **Cross-cutting feature marker**: yes. +- **Systems touched**: + - `ManagedEnvironmentLinks` + - `WorkspaceIntendedUrl` + - `WorkspaceRedirectResolver` + - `AdminPanelProvider` + - resource/page `shouldRegisterNavigation()` owners + - `WorkspaceOverviewBuilder` + - `workspace-overview.blade.php` + - `setAdminEnvironmentContext()` and focused guard/browser tests +- **Shared abstractions reused**: current canonical route helpers, current route-scope navigation owners, existing guard-test patterns, and the existing test helper vocabulary. +- **New abstraction introduced? why?**: none planned. Only extend existing route-scope or navigation owners if a concrete live seam is discovered. +- **Why the existing abstraction was sufficient or insufficient**: canonical ownership already exists for routes, links, and intended URLs. The remaining issue is final proof, classification, and narrow residual cleanup. +- **Bounded deviation / spread control**: technical `Tenant` owners remain allowed only when documented as internal, provider-owned, historical, or regression-guard only. + +## OperationRun UX Impact + +- **Touches OperationRun start/completion/link UX?**: yes, canonical operations link ownership and intended-URL normalization only. +- **Central contract reused**: `OperationRunLinks` and `ManagedEnvironmentLinks::operationsUrl(...)`. +- **Delegated UX behaviors**: no local lifecycle or notification behavior; only prove that `/admin/operations` resolves safely and retired tenant-scoped operation paths stay dead. +- **Surface-owned behavior kept local**: workspace overview summary copy and existing navigation only. +- **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 tenant ID, Entra tenant ID, provider target-tenant scope wording, Graph permission wording. +- **Platform-core seams**: workspace/environment navigation and operator vocabulary, canonical route ownership, intended URL handling, workspace overview copy. +- **Neutral platform terms / contracts preserved**: workspace, environment, managed environment, provider connection, required permissions, diagnostics, operations, findings, evidence, reviews. +- **Retained provider-specific semantics and why**: provider-specific `tenant` wording remains only when it describes external Microsoft/Entra truth. +- **Bounded extraction or follow-up path**: no new framework. Structural rename remains a separate future topic if ever promoted. + +## Constitution Check + +*GATE: Must pass before runtime implementation and re-check before close-out.* + +- Inventory-first: no inventory or snapshot truth changes. +- Read/write separation: no new write workflow. Any touched destructive action labels must preserve existing confirmation, authorization, and audit behavior. +- Graph contract path: no new Graph calls. +- Deterministic capabilities: no capability-resolution change. +- Workspace isolation: workspace routes remain workspace-safe and must not be overridden by remembered environment context. +- Tenant isolation: canonical environment routes remain entitlement-checked; retired tenant routes remain unavailable. +- RBAC-UX: non-member/out-of-scope remains 404; member missing capability remains 403. +- OperationRun: no new lifecycle semantics; workspace operations links remain canonical. +- Proportionality / no premature abstraction: use the existing route and navigation owners; no new framework is justified. +- Persisted truth: no new application persistence. The only new artifact is the spec-local cutover audit. +- Provider boundary: generic operator vocabulary must stay environment-first; provider-specific tenant wording remains bounded. +- Test governance: validation remains in the narrowest honest lanes; no hidden full-suite requirement. +- Filament-native UI: Filament remains v5 on Livewire v4, no new panel, no ad hoc styling. + +## Filament v5 Output Contract + +- **Livewire compliance**: Filament v5 targets Livewire v4.0+; current app has Livewire 4.1.4. +- **Provider registration location**: provider registration remains in `apps/platform/bootstrap/providers.php`; this package must not restore any retired panel provider. +- **Globally searchable resources**: any remaining technical `TenantResource` surface must stay non-primary and must not leak old routes through global search. +- **Destructive actions**: any touched restore/remove/archive action must still use `->action(...)`, `->requiresConfirmation()`, and server-side authorization. +- **Asset strategy**: unchanged. If implementation unexpectedly registers assets, deployment must include `cd apps/platform && php artisan filament:assets`. +- **Testing plan**: touched pages/resources are covered with focused Pest/Filament tests, plus explicit route/helper/copy guards and selected browser anchors. + +## Test Governance Check + +- **Test purpose / classification by changed surface**: Feature guards for route/helper/copy regression, Feature/Filament and Feature/Workspaces for navigation proof, Browser for touched visible anchors only. +- **Affected validation lanes**: Feature/Guards, Feature/Workspaces, Feature/ProviderConnections, Feature/RequiredPermissions, Feature/Filament, selected Browser anchors. +- **Why this lane mix is the narrowest sufficient proof**: the goal is final cutover sealing, not broad behavior changes. Focused lanes prove route, navigation, copy, and allowlist integrity without reopening unrelated suites. +- **Narrowest proving command(s)**: + +```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/Filament/PanelNavigationSegregationTest.php \ + tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php +``` + +- **Fixture / helper / factory / seed / context cost risks**: do not widen `setAdminEnvironmentContext()` or add new expensive defaults to prove this package. +- **Expensive defaults or shared helper growth introduced?**: none planned. +- **Heavy-family additions, promotions, or visibility changes**: none planned; browser runs stay anchor-only. +- **Surface-class relief / special coverage rule**: `standard-native-filament` plus `global-context-shell`; browser only when touched. +- **Closing validation and reviewer handoff**: rerun the focused proof pack, final scans, selected lanes, browser anchors if touched, Pint, and `git diff --check`. +- **Budget / baseline / trend follow-up**: none expected beyond recording any unexpected browser or guard runtime drift. +- **Review-stop questions**: Did any active runtime legacy seam remain? Did workspace navigation still leak environment-owned entries? Did any active product copy remain tenant-first without classification? Did the proof pack stay focused? +- **Escalation path**: document-in-feature for allowed references; blocked-by-prerequisite or blocked-by-runtime-finding if 298 is not landed or a live seam remains. +- **Active feature PR close-out entry**: Guardrail / Final Cutover Seal / Smoke Coverage. +- **Why no dedicated follow-up spec is needed**: this package exists specifically to close the current cutover topic without reopening broader rename work. + +## Project Structure + +### Documentation (this feature) + +```text +specs/299-managed-environment-cutover-final-seal/ +├── spec.md +├── plan.md +├── tasks.md +├── final-cutover-audit.md +└── checklists/ + └── requirements.md +``` + +### Source Code (repository root) + +Likely touched implementation surfaces if a concrete remaining seam is found: + +```text +apps/platform/app/ +├── Providers/Filament/AdminPanelProvider.php +├── Support/ManagedEnvironmentLinks.php +├── Support/Workspaces/WorkspaceIntendedUrl.php +├── Support/Workspaces/WorkspaceRedirectResolver.php +├── Support/Workspaces/WorkspaceOverviewBuilder.php +└── Filament/** + +apps/platform/resources/views/ +└── filament/pages/workspace-overview.blade.php + +apps/platform/tests/ +├── Pest.php +├── Feature/Guards/** +├── Feature/Workspaces/** +├── Feature/Filament/** +└── Browser/** +``` + +**Structure Decision**: stay inside the existing Laravel/Filament app and spec package shape; do not create new application folders or dependencies. + +## Complexity Tracking + +| Violation | Why Needed | Simpler Alternative Rejected Because | +|---|---|---| +| Spec-local `final-cutover-audit.md` | The repo needs one durable final acceptance record that separates active drift from allowed references | A one-paragraph close-out would not preserve the allowlist or proof obligations | +| Cross-cutting guard pack | The remaining risk spans routes, navigation, intended URLs, copy, and tests | One local cleanup would not seal the cutover topic | + +## Phase 0: Start Condition And Safety Gate + +1. Run: + +```bash +git status --short --branch +git diff --stat +git log -1 --oneline +``` + +2. Confirm Spec 298 changes are committed/merged into the implementation base or intentionally isolated in a separate clean worktree/session branch. +3. Stop if unrelated uncommitted changes are present. +4. Read: + +```text +.specify/memory/constitution.md +specs/297-managed-environment-canonical-route-cutover/ +specs/298-managed-environment-terminology-copy-cleanup/ +``` + +## Phase 1: Baseline Final Cutover Audit + +Refresh `final-cutover-audit.md` before runtime edits: + +```bash +git status --short --branch +git diff --stat + +cd apps/platform +./vendor/bin/sail artisan route:list | rg "admin/t|admin/tenants|workspaces/.*/environments|operations|provider-connections|required-permissions" + +rg "TenantPanelProvider|panel:\s*'tenant'|panel:\s*\"tenant\"|/admin/t/|/admin/tenants|filament\.admin\.resources\.tenants|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext" app resources routes --glob '!vendor' --glob '!node_modules' + +rg "setTenantPanelContext|panel:\s*'tenant'|panel:\s*\"tenant\"|/admin/t/|/admin/tenants|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl" tests --glob '!vendor' --glob '!node_modules' + +rg "Tenant dashboard|Tenant detail|Open tenant|Select tenant|Tenant scope|Tenant memberships|Remove tenant|Restore tenant|Accessible tenants|affected tenants" app resources lang tests --glob '!vendor' --glob '!node_modules' + +rg "shouldRegisterNavigation|getNavigationGroup|getNavigationLabel|getNavigationSort|Filament::getTenant|TenantPageCategory|setAdminEnvironmentContext|WorkspaceContext" app tests --glob '!vendor' --glob '!node_modules' +``` + +Implementation intent: + +- if the runtime app tree is still clean, keep it that way and move to proof depth +- if a live runtime seam appears, fix only that seam +- classify every remaining hit as active runtime, test-only, copy-only, provider-specific, internal, historical, or guard-only + +## Phase 2: Runtime Final Seal + +- Inspect `ManagedEnvironmentLinks`, `WorkspaceIntendedUrl`, `WorkspaceRedirectResolver`, and `AdminPanelProvider` as the current canonical owners. +- Keep `TenantPanelProvider` absent from the runtime app tree and registration. +- Verify `TenantResource`, `TenantDashboard`, and `TenantRequiredPermissions` remain technical-only or route through canonical owners without reviving retired route families. +- If a direct runtime legacy hit still exists, replace it with the existing canonical helper or route owner. Do not add compatibility routes or aliases. +- Re-run the focused runtime source scan and update `final-cutover-audit.md`. + +## Phase 3: Navigation Seal + +- Re-prove workspace vs environment navigation using the current route, not stale remembered environment state. +- Keep workspace surfaces limited to workspace-owned navigation: Overview, Operations, Alerts, Audit Log, Governance inbox, Customer reviews, Manage workspaces, Integrations, Settings. +- Keep environment-owned navigation limited to canonical environment routes. +- Reuse existing `shouldRegisterNavigation()` and `TenantPageCategory` / route-scope owners rather than inventing a new navigation framework. +- Update focused tests or resource/page navigation owners only when the final proof surfaces a live leak. + +## Phase 4: Intended URL And Helper Retirement Proof + +- Re-prove that `WorkspaceIntendedUrl` and `WorkspaceRedirectResolver` reject `/admin/t...` and `/admin/tenants...` paths. +- Re-prove that `/admin/operations` only normalizes to the canonical workspace operations route. +- Verify `setAdminEnvironmentContext()` remains the current helper. +- Leave `setTenantPanelContext` only inside explicit forbidden-pattern guards, if anywhere. + +## Phase 5: Product Copy Boundary And Allowlist + +- Start with the confirmed active workspace-overview copy seams in `WorkspaceOverviewBuilder.php` and `workspace-overview.blade.php`. +- Replace touched product-facing tenant-first wording with environment-first wording where the subject is a managed environment. +- Leave provider-specific Microsoft/Entra tenant terms untouched when the provider is the subject. +- Classify technical/internal/historical/guard-only `Tenant` references in `final-cutover-audit.md` rather than trying to erase the entire repo. + +## Phase 6: Final Proof Pack + +Run the focused proof pack: + +```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/Guards/WorkspaceEnvironmentNavigationSegregationTest.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 + +./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 artisan test --compact \ + tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php \ + tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php \ + tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php \ + tests/Browser/Spec192RecordPageHeaderDisciplineSmokeTest.php + +./vendor/bin/sail bin pint --dirty --format agent +``` + +Then from the repo root: + +```bash +git diff --check +``` + +Finally rerun the route/source scans from Phase 1 and update the audit with the final clean or blocked state. + +## Phase 7: Close-Out + +- Update `final-cutover-audit.md` with final findings, allowlists, commands, and outcomes. +- Record the Filament v5 output contract in the implementation summary. +- End with one explicit decision string: + - `merge-ready; managed environment cutover sealed` + - `merge-ready with documented allowed internal Tenant references` + - `blocked by active legacy runtime finding` + - `blocked by navigation context leak` + +## Explicit Follow-Ups / Out Of Scope + +- DB/model/table rename from `Tenant` to `ManagedEnvironment` +- broad historical or documentation copy sweep +- provider-architecture rewrite +- new RBAC model or new product navigation framework +- new feature work unrelated to cutover closure diff --git a/specs/299-managed-environment-cutover-final-seal/spec.md b/specs/299-managed-environment-cutover-final-seal/spec.md new file mode 100644 index 00000000..65625a89 --- /dev/null +++ b/specs/299-managed-environment-cutover-final-seal/spec.md @@ -0,0 +1,351 @@ +# Feature Specification: Managed Environment Cutover Final Seal & Regression Guard Pack + +**Feature Branch**: `299-managed-environment-cutover-final-seal` +**Created**: 2026-05-13 +**Status**: Ready +**Input**: User-provided Spec 299 prompt: final hardening, regression guards, and cutover closure for the workspace-first managed-environment migration after Specs 297 and 298. + +## Spec Candidate Check *(mandatory - SPEC-GATE-001)* + +- **Problem**: The managed-environment cutover is close to done, but the repo still lacks one final seal package that proves the runtime core stays off retired tenant routes, that workspace surfaces remain workspace-scoped even with stale environment context, and that remaining `Tenant` terms are either removed from active product UI or explicitly allowlisted. +- **Today's failure**: Contributors can still see broad legacy test and copy surfaces, including technical `TenantResource::getUrl(...)` / `TenantDashboard::getUrl(...)` usages in tests, workspace overview copy such as `Accessible tenants`, and route-scope-sensitive navigation that must stay sealed against remembered environment context. Without a final proof pack, future work can accidentally revive retired product truths even if the app tree currently looks clean. +- **User-visible improvement**: Operators get one final, credible cutover state: workspace-first navigation on workspace pages, explicit environment drill-down on canonical environment routes, no revived `/admin/t...` or `/admin/tenants...` runtime paths, and environment-first copy on the touched active surfaces. +- **Smallest enterprise-capable version**: Refresh a final cutover audit, prove the runtime app tree remains clean, seal any last workspace-vs-environment navigation or intended-URL seam, bound active product copy, classify remaining technical/provider/historical `Tenant` references, and add the final focused regression guard pack. +- **Explicit non-goals**: No database/model/table rename from `Tenant` to `ManagedEnvironment`, no compatibility routes, no dual-read or dual-write, no new product workflow, no new RBAC model, no provider-model rewrite, no package execution, no guided operations, no AI workflow, no generic historical-doc cleanup, and no raw full-suite requirement by default. +- **Permanent complexity imported**: One spec-local `final-cutover-audit.md` artifact, a final focused guard/validation pack, and at most narrow adjustments to existing navigation or link owners if a concrete remaining seam is found. No new application persistence, status family, framework, or broad UI layer is introduced. +- **Why now**: Specs 297 and 298 already did the cutover and terminology cleanup work. The next safe move is to close the topic with one final seal package before unrelated features reintroduce tenant-first runtime or workspace/environment drift. +- **Why not local**: The remaining risk spans canonical links, intended URL handling, navigation registration, workspace overview copy, guard tests, browser smokes, and explicit allowlists. A one-file cleanup would not make the cutover traceable or durable. +- **Approval class**: Cleanup +- **Red flags triggered**: cross-cutting final hardening across routes, navigation, tests, and copy. Defense: the scope is intentionally closure-only, tied to already prepared Specs 297 and 298, and explicitly forbids reopening broader rename or architecture work. +- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12** +- **Decision**: approve + +## Start Condition + +- Spec 299 implementation may start only after Spec 298 changes are either committed/merged into the implementation base or explicitly isolated in a separate clean worktree/session branch. +- Do not begin 299 implementation on a dirty intermediate 298 state. +- Before runtime edits, run `git status --short --branch` and `git diff --stat`, then document any pre-existing changes that are intentionally carried into the 299 implementation branch. + +## Summary + +Spec 299 is the final seal package for the managed-environment cutover. It does not open a new product lane. It proves and, only if necessary, finishes the current product truth: + +- no active `/admin/t/...` or `/admin/tenants/...` runtime paths +- no active runtime link generators back to retired tenant surfaces +- no workspace-level environment navigation leak from stale session or `Filament::getTenant()` state +- no callable retired `setTenantPanelContext()` helper +- no product-facing tenant-first wording on the touched active cutover surfaces +- an explicit allowlist for remaining technical, provider-specific, guard-only, or historical `Tenant` references +- a final proof pack that keeps the cutover sealed + +## Spec Scope Fields *(mandatory)* + +- **Scope**: canonical-view +- **Primary Routes**: + - Retired and forbidden as active product truth: `/admin/t/...`, `/admin/tenants/...` + - Canonical workspace home: `/admin/workspaces/{workspace}` + - Canonical managed-environment list/detail: `/admin/workspaces/{workspace}/environments`, `/admin/workspaces/{workspace}/environments/{environment}` + - Canonical environment child routes: `/admin/workspaces/{workspace}/environments/{environment}/required-permissions`, `/diagnostics`, `/access-scopes`, and other repo-real child routes + - Canonical workspace operations: `/admin/workspaces/{workspace}/operations` and `/admin/workspaces/{workspace}/operations/{run}` +- **Data Ownership**: + - No persistence change. + - Workspace remains the primary SaaS context. + - Managed environment remains the secondary managed target context. + - Internal `Tenant` model/class/table naming may remain technical implementation truth where a structural rename is out of scope. +- **RBAC**: + - Workspace membership remains the primary access boundary. + - Managed-environment access scope remains narrowing-only and must not become role authority. + - Non-member or out-of-scope access remains 404. + - Established member missing capability remains 403. + +For canonical-view specs: + +- **Default filter behavior when tenant-context is active**: remembered environment context may survive as a secondary drill-down hint, but route scope decides navigation, intended URL acceptance, and which surface is primary. Workspace surfaces remain workspace-scoped even if an environment is remembered. +- **Explicit entitlement checks preventing cross-tenant leakage**: `WorkspaceIntendedUrl` and `WorkspaceRedirectResolver` must reject retired tenant paths, `ManagedEnvironmentLinks` must derive canonical URLs from explicit workspace/environment scope, and any canonical environment or operations route must keep existing workspace and managed-environment entitlement checks. + +## Goals + +### G1 - Final active legacy scan is clean + +The final route/source scans must show no active runtime hits for `/admin/t/...`, `/admin/tenants/...`, `TenantPanelProvider`, retired tenant panel IDs, retired runtime URL generators, or `setTenantPanelContext()` in the runtime app tree. + +### G2 - Workspace/environment navigation is sealed + +Workspace surfaces may expose workspace-owned navigation only. Environment-owned primary navigation must appear only on canonical environment routes. + +### G3 - Canonical managed-environment link ownership is enforced + +Runtime links must stay owned by `ManagedEnvironmentLinks`, `RequiredPermissionsLinks`, `OperationRunLinks`, or the current canonical route owner. Retired tenant route generators must not become active product truth again. + +### G4 - Intended URL handling cannot revive retired paths + +Stored intended URLs must reject retired tenant paths and only normalize safe canonical workspace operations routes. + +### G5 - Product-facing tenant terminology is bounded + +Touched active product surfaces should use environment-first wording where the subject is a managed environment. Provider-specific Microsoft/Entra tenant wording remains allowed only when the external provider is the subject. + +### G6 - Guard pack prevents regression + +The final proof pack must make it difficult to reintroduce retired tenant routes, tenant-panel helper semantics, or workspace/environment navigation leaks. + +## Required Artifact + +Create or update: + +```text +specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md +``` + +This audit must track: + +- active runtime legacy findings +- test legacy findings +- copy findings +- allowed references +- final route/source scan results +- final validation commands and outcomes + +## Cross-Cutting / Shared Pattern Reuse *(mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write `N/A - no shared interaction family touched`)* + +- **Cross-cutting feature?**: yes +- **Interaction class(es)**: navigation registration, canonical environment links, intended URL handling, workspace overview summary metrics and copy, guard tests, browser smokes, and allowlist auditing. +- **Systems touched**: + - `apps/platform/app/Support/ManagedEnvironmentLinks.php` + - `apps/platform/app/Support/Workspaces/WorkspaceIntendedUrl.php` + - `apps/platform/app/Support/Workspaces/WorkspaceRedirectResolver.php` + - `apps/platform/app/Providers/Filament/AdminPanelProvider.php` + - resource/page `shouldRegisterNavigation()` owners under `apps/platform/app/Filament/**` + - `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` + - `apps/platform/resources/views/filament/pages/workspace-overview.blade.php` + - `apps/platform/tests/Pest.php` + - `apps/platform/tests/Feature/Filament/PanelNavigationSegregationTest.php` + - focused guard/browser tests that still mention retired tenant routes or technical `Tenant*` owners +- **Existing pattern(s) to extend**: `ManagedEnvironmentLinks`, `WorkspaceIntendedUrl`, `WorkspaceRedirectResolver`, `OperationRunLinks`, `TenantPageCategory`, existing navigation registration closures, `setAdminEnvironmentContext()`, and the current guard-test pattern. +- **Shared contract / presenter / builder / renderer to reuse**: the existing canonical route helpers and route-scope navigation owners named above; no new shared framework is justified. +- **Why the existing shared path is sufficient or insufficient**: the runtime app tree is already mostly aligned to the canonical owners. The missing closure is one final proof/audit layer, plus narrow fixes only if the final baseline still exposes a live seam. +- **Allowed deviation and why**: technical `Tenant` class/model/table names, provider-specific Microsoft/Entra tenant language, and guard-regex strings may remain when explicitly classified in `final-cutover-audit.md`. +- **Consistency impact**: runtime links, workspace/environment navigation, intended URL handling, workspace overview copy, and the focused proof pack must all agree on the same workspace-first / environment-drilldown product truth. +- **Review focus**: reviewers must verify that `299` does not reopen broader rename work, does not preserve compatibility routes, and does not leave any unclassified product-facing tenant-first seams. + +## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)* + +- **Touches OperationRun start/completion/link UX?**: yes, link and redirect ownership only. +- **Shared OperationRun UX contract/layer reused**: `OperationRunLinks` plus `ManagedEnvironmentLinks::operationsUrl(...)` for workspace-scoped operations entry points. +- **Delegated start/completion UX behaviors**: no new toast, status, or notification behavior. The only required proof is that legacy intended URLs do not restore retired tenant-scoped operation paths and that workspace operations remain canonical. +- **Local surface-owned behavior that remains**: workspace overview and environment detail surfaces keep their current local labels and calls to the shared link owners. +- **Queued DB-notification policy**: N/A. +- **Terminal notification path**: unchanged. +- **Exception required?**: none. + +## Provider Boundary / Platform Core Check *(mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write `N/A - no shared provider/platform boundary touched`)* + +- **Shared provider/platform boundary touched?**: yes +- **Boundary classification**: mixed +- **Seams affected**: operator vocabulary, provider connection scope language, required-permissions wording, workspace/environment route labels, and allowed technical `Tenant` references. +- **Neutral platform terms preserved or introduced**: workspace, managed environment, environment, provider connection, required permissions, diagnostics, access scope, operation, finding, evidence, review. +- **Provider-specific semantics retained and why**: Microsoft tenant ID, Entra tenant ID, and provider target-tenant wording remain allowed only where the external provider is the subject. +- **Why this does not deepen provider coupling accidentally**: the spec narrows `tenant` wording to provider-owned or technical-only seams while keeping platform-core navigation and operator UI environment-first. +- **Follow-up path**: document-in-feature for allowed technical/provider references; follow-up-spec only for future structural DB/model rename. + +## UI / Surface Guardrail Impact *(mandatory when operator-facing surfaces are changed; otherwise write `N/A`)* + +| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note | +|---|---|---|---|---|---|---| +| Workspace overview and sidebar scope | yes | Native Filament + shared builders | navigation, summary metrics, context copy | shell, page | no | closure-only; no redesign | +| Canonical environment detail/readiness surfaces | yes | Native Filament + shared route helpers | environment navigation, page/back links, detail copy | page, detail | no | prove the current environment drill-down remains canonical | +| Final cutover audit and guard pack | no direct operator surface | N/A | guard tests, scans, audit evidence | docs, tests | no | repo-proof only | + +## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)* + +| 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 overview and sidebar scope | Primary Decision Surface | Operator chooses whether to stay at workspace scope or drill into one environment | workspace summary, allowed workspace navigation, explicit environment drill-down affordance | environment details, diagnostics, and evidence remain on child surfaces | Primary because the workspace home is the first scope decision after the cutover | Workspace first, environment second | prevents stale environment context from hijacking workspace navigation | +| Canonical environment detail/readiness surfaces | Secondary Context Surface | Operator follows one selected environment into detail, permissions, or diagnostics | selected environment identity and current environment-owned navigation | diagnostics, provider detail, raw support content | Secondary because the environment route supports a decision already made at workspace scope | explicit drill-down from workspace to environment | avoids mixed workspace/environment affordances on the same page | + +## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)* + +| 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 overview and sidebar scope | operator-MSP, support-platform | workspace summary metrics, calmness/attention framing, workspace-owned navigation | environment-specific follow-up only through explicit drill-down | none beyond existing support tools | choose environment or view workspace operations | environment-owned navigation on workspace surfaces | one scope truth per route | +| Canonical environment detail/readiness surfaces | operator-MSP, support-platform | selected environment context, current readiness or workload state | provider readiness, diagnostics, evidence | provider/raw support detail only where already allowed | continue environment workflow on the current canonical route | workspace-only actions remain secondary or absent | current environment route owns the environment story | + +## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)* + +| 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 overview and sidebar scope | Dashboard / Navigation | Workspace-first overview | choose environment or review operations | direct page load on workspace home | N/A | current summary drill-downs and navigation items | unchanged; existing destructive actions stay elsewhere | `/admin/workspaces/{workspace}` | N/A | workspace only | Workspace / Environment | workspace-first scope and allowed nav | none | +| Canonical environment detail/readiness surfaces | Detail / Readiness | Environment-scoped detail | open the current environment area or remediate readiness | canonical environment route | N/A | existing detail/header actions | unchanged and still confirmation-gated where destructive | `/admin/workspaces/{workspace}/environments` | `/admin/workspaces/{workspace}/environments/{environment}` | workspace + environment | Environment / Managed environment | selected environment and current child navigation | none | + +## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)* + +| 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 overview and sidebar scope | Workspace operator | Decide whether to stay at workspace scope or drill into one environment | overview | What needs attention in this workspace before I narrow scope? | workspace summary metrics, allowed navigation, scope framing | none beyond existing linked surfaces | calmness, attention, activity | none | Choose environment, View operations | none added | +| Canonical environment detail/readiness surfaces | Workspace operator | Continue environment-specific investigation or remediation | environment detail/readiness | What needs action in this environment? | environment identity, environment navigation, current readiness/action context | provider detail, diagnostics, support/raw detail | readiness, governance result, activity | existing environment-owned actions only | existing page-owned actions | existing destructive actions only | + +## Proportionality Review *(mandatory when structural complexity is introduced)* + +- **New source of truth?**: no +- **New persisted entity/table/artifact?**: yes, one spec-local audit artifact only +- **New abstraction?**: no application abstraction by default; the implementation may only extend existing route-scope helpers if a live seam is confirmed +- **New enum/state/reason family?**: no +- **New cross-domain UI framework/taxonomy?**: no +- **Current operator problem**: without one final proof package, the cutover can quietly drift back toward retired tenant routes or mixed workspace/environment truth. +- **Existing structure is insufficient because**: Specs 297 and 298 prepared and cleaned the main seams, but the remaining test, copy, and regression burden is spread across multiple owners and lacks one final acceptance artifact. +- **Narrowest correct implementation**: one closure spec that proves the app tree stays clean, fixes only confirmed remaining seams, and classifies everything else. +- **Ownership cost**: one audit file plus focused guard maintenance. +- **Alternative intentionally rejected**: a broad rename or another foundational cutover spec. That would reopen work already intentionally kept out of scope. +- **Release truth**: current-release closure in a pre-production environment. + +### Compatibility posture + +This feature assumes a pre-production environment. + +Backward compatibility routes, legacy helper aliases, migration shims, historical fixture preservation for runtime behavior, and compatibility-specific runtime tests are out of scope unless they already exist as explicit negative regression guards. + +Canonical replacement or explicit rejection is preferred over preservation. + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Retired Tenant Runtime Stays Sealed (Priority: P1) + +As a maintainer, I need the final cutover proof to show that no active runtime route or link generator revives `/admin/t/...` or `/admin/tenants/...` behavior. + +**Why this priority**: This is the core cutover promise. If it regresses, the product truth is no longer workspace-first. + +**Independent Test**: Run the focused route/source/guard proof pack and verify representative retired paths return 404 while canonical environment and workspace operations links remain valid. + +**Acceptance Scenarios**: + +1. **Given** an authenticated workspace member, **When** they request a representative retired tenant path such as `/admin/t/{id}` or `/admin/tenants/{id}`, **Then** the response is 404. +2. **Given** the runtime app tree, **When** the final legacy scan runs, **Then** no active retired route/helper pattern remains under `apps/platform/app`, `apps/platform/resources`, or `apps/platform/routes`. + +--- + +### User Story 2 - Workspace Navigation Wins Over Stale Environment Context (Priority: P1) + +As an operator, I need workspace pages to stay workspace-scoped even when the session remembers an environment or `Filament::getTenant()` still holds one. + +**Why this priority**: Mixed workspace/environment navigation makes the cutover feel unfinished and can mislead operators about scope. + +**Independent Test**: Exercise workspace overview and canonical environment routes with remembered environment context and verify the sidebar changes by route scope, not by stale environment state. + +**Acceptance Scenarios**: + +1. **Given** a remembered environment and a workspace overview request, **When** the page renders, **Then** only workspace-owned navigation is visible. +2. **Given** the canonical environment detail route, **When** the page renders, **Then** environment-owned navigation appears again. + +--- + +### User Story 3 - Remaining Tenant References Are Honest And Bounded (Priority: P2) + +As a reviewer, I need every remaining `Tenant` reference to be either fixed or explicitly classified so future work can tell the difference between product drift and allowed technical/provider/historical wording. + +**Why this priority**: The remaining legacy footprint is broadest in tests and copy. Unclassified references create noisy audits and invite accidental regressions. + +**Independent Test**: Refresh the final audit, run the copy/test scans, and verify every remaining hit is marked `fixed`, `allowed-provider-term`, `allowed-internal-model`, `allowed-historical`, or `allowed-regression-guard`. + +**Acceptance Scenarios**: + +1. **Given** an active product-facing workspace or environment surface, **When** the cutover copy scan runs, **Then** remaining tenant-first wording is either removed or intentionally allowed because it is provider-specific. +2. **Given** a technical test or guard, **When** it still mentions `TenantResource`, `TenantDashboard`, `/admin/t`, or `setTenantPanelContext`, **Then** the final audit states why that reference remains safe. + +## Edge Cases + +- What happens when a remembered environment belongs to the current workspace but the current route is workspace-only? The page must stay workspace-scoped and suppress environment-owned navigation. +- What happens when an intended URL points to `/admin/operations` with query data? It may normalize only to the canonical workspace operations route, not to any retired tenant-scoped operations path. +- What happens when a string contains `tenant` but the subject is Microsoft/Entra provider identity? The wording may remain only when the external provider is the actual subject. + +## Functional Requirements + +- **FR-001 No active retired tenant routes**: `route:list` and focused request tests must prove that `/admin/t/...` and `/admin/tenants/...` are not active product routes. +- **FR-002 Tenant runtime owners stay technical-only or retired**: if `TenantResource`, `TenantDashboard`, or `TenantRequiredPermissions` remain in code, they must not restore primary admin navigation, global search entries, or retired route families. +- **FR-003 Canonical managed-environment link ownership is preserved**: runtime environment links remain owned by `ManagedEnvironmentLinks`, `RequiredPermissionsLinks`, `OperationRunLinks`, or the current canonical route owner. +- **FR-004 Workspace intended URL rejection is final**: `WorkspaceIntendedUrl` and `WorkspaceRedirectResolver` must reject retired tenant paths and only normalize safe workspace operations routes. +- **FR-005 Route-scope-first navigation is final**: workspace routes render workspace-only navigation, canonical environment routes render environment-owned navigation, and stale remembered environment context does not override route scope. +- **FR-006 Retired tenant-panel helper remains retired**: no callable `setTenantPanelContext()` alias may exist; remaining string hits may survive only inside explicit forbidden-pattern guards. +- **FR-007 Product-facing tenant terminology is bounded**: touched active UI surfaces use environment-first wording where the subject is a managed environment. +- **FR-008 Remaining tenant references are explicitly allowlisted**: the final audit must classify every remaining `Tenant` reference that survives the final scans. +- **FR-009 No broad refactors**: the implementation must not reopen DB/model rename, provider refactor, package execution, guided operations, or generic historical-doc cleanup. +- **FR-010 Final cutover audit is maintained**: `final-cutover-audit.md` must capture baseline findings, final findings, validation commands, and the final decision string. + +## Non-Functional Requirements + +- The validation mix must stay focused to guard/feature/browser anchors relevant to the cutover; no raw full suite is required unless explicitly requested. +- Filament v5 / Livewire v4 compliance remains explicit, and provider registration stays in `apps/platform/bootstrap/providers.php`. +- The package must stay implementation-bounded: fix only confirmed remaining seams and document everything else. +- Final proof commands must be explicit, reproducible, and small enough for a bounded implementation loop. + +## Out Of Scope + +- Database, model, namespace, or table rename from `Tenant` to `ManagedEnvironment` +- Migration or historical data rewrite +- Reintroducing compatibility routes or compatibility aliases +- New product features, new operations workflows, or new navigation families +- Broad localization project or historical docs/spec rewrite +- Provider architecture refactor or new RBAC model +- Raw full-suite enforcement without explicit user instruction + +## Acceptance Criteria + +- **AC-001**: `route:list` does not expose `/admin/t` or `/admin/tenants` as active routes. +- **AC-002**: no active runtime route/helper pattern for retired tenant surfaces remains under `apps/platform/app`, `apps/platform/resources`, or `apps/platform/routes`. +- **AC-003**: `ManagedEnvironmentLinks`, `WorkspaceIntendedUrl`, and `WorkspaceRedirectResolver` remain the canonical owners for environment detail, intended URLs, and workspace operations redirects. +- **AC-004**: workspace overview does not show environment-owned navigation, even with remembered environment session data or `Filament::getTenant()` state. +- **AC-005**: canonical environment routes still show the intended environment-owned navigation entries. +- **AC-006**: no callable `setTenantPanelContext()` helper remains. +- **AC-007**: touched active product-facing copy no longer uses tenant-first wording when the subject is a managed environment. +- **AC-008**: every remaining `Tenant` reference that survives final scans is documented as allowed technical, provider-specific, historical, or regression-guard only. +- **AC-009**: the focused proof pack, feature lanes, browser anchors, final route/source scans, Pint, and `git diff --check` all pass or the failure is explicitly recorded as a blocker. +- **AC-010**: the final report ends with one explicit decision string for the cutover seal. + +## Success Criteria + +- Reviewers can answer "is the managed-environment cutover sealed?" from one spec package without reopening 297 or 298. +- The final audit distinguishes active product drift from allowed technical/provider/historical references. +- The implementation loop can stop with a clear decision instead of vague residual cleanup. + +## Risks + +- Spec 298 runtime changes may not yet be landed in the exact implementation base, so 299 could otherwise mix closure work with unfinished navigation/copy cleanup. +- The remaining test surface is broad and uses technical `Tenant*` owners heavily; without disciplined classification, the audit can become noisy or overreach. +- Workspace overview copy and navigation are easy to drift because they mix workspace summary, environment drill-down, and operations entry points. + +## Assumptions + +- Specs 297 and 298 remain authoritative context and must not be rewritten back into preparation-only state. +- The runtime app tree already follows the canonical route owners unless the final baseline uncovers a concrete exception. +- Internal `Tenant` model/class naming is still allowed as technical implementation detail for this release. + +## Open Questions + +- None blocking. The only prerequisite boundary is implementation timing: Spec 298 changes must be committed or cleanly isolated before 299 runtime work begins. + +## Definition Of Done + +Spec 299 is done when: + +1. The baseline audit is recorded in `final-cutover-audit.md`. +2. Any confirmed active runtime legacy seam is fixed without restoring compatibility paths. +3. Workspace/environment navigation segregation is re-proven. +4. Intended URL rejection is re-proven. +5. Touched active product copy is environment-first. +6. Remaining `Tenant` references are classified in the allowlist. +7. The focused proof pack, lanes, browser anchors, scans, Pint, and diff check are recorded. +8. The final decision string is explicit. + +## Final Output Required + +At implementation close-out, report: + +1. Commands run and outcomes. +2. Runtime legacy closure findings. +3. Workspace vs environment navigation classification. +4. Remaining allowed `Tenant` references and why they remain. +5. Test results. +6. One final decision string: + - `merge-ready; managed environment cutover sealed` + - `merge-ready with documented allowed internal Tenant references` + - `blocked by active legacy runtime finding` + - `blocked by navigation context leak` diff --git a/specs/299-managed-environment-cutover-final-seal/tasks.md b/specs/299-managed-environment-cutover-final-seal/tasks.md new file mode 100644 index 00000000..63b4b931 --- /dev/null +++ b/specs/299-managed-environment-cutover-final-seal/tasks.md @@ -0,0 +1,134 @@ +--- +description: "Task list for Managed Environment Cutover Final Seal & Regression Guard Pack" +--- + +# Tasks: Managed Environment Cutover Final Seal & Regression Guard Pack + +**Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/299-managed-environment-cutover-final-seal/` +**Prerequisites**: `spec.md`, `plan.md`, `final-cutover-audit.md`, `checklists/requirements.md` + +**Tests**: Required (Pest) for route/helper/navigation/copy guard changes. Browser smoke is required only if touched visible browser anchors or selectors change. +**Operations**: No new `OperationRun` lifecycle behavior. Existing workspace operations links and intended URL handling must keep the shared canonical contracts if touched. +**RBAC**: No authorization model change. Existing 404/403 semantics, capability checks, and destructive action authorization must remain intact. +**Filament / Panel Guardrails**: Filament remains v5 on Livewire v4. Provider registration remains in `apps/platform/bootstrap/providers.php`. No new panel. No asset-strategy change unless explicitly documented. +**Review Outcome**: documentation-required-exception +**Workflow Outcome**: keep +**Test-governance Outcome**: keep + +## Test Governance Checklist + +- [x] Lane assignment is named and is the narrowest sufficient proof for each changed behavior. +- [x] New or changed tests stay in the smallest honest family; browser additions are explicit and anchor-only. +- [x] Shared helpers, factories, seeds, fixtures, provider setup, workspace context, session state, and capability defaults stay cheap by default. +- [x] Planned validation commands cover route, helper, navigation, copy, and allowlist changes without pulling in unrelated suite cost. +- [x] The declared surface test profiles or `standard-native-filament` relief are explicit. +- [x] Any material runtime, budget, baseline, trend, or escalation note is recorded in the active spec close-out. + +## Phase 1: Safety Gate And Baseline Audit + +**Purpose**: Start from a clean, dependency-safe implementation base and capture repo truth before runtime edits. + +- [x] T001 Run `git status --short --branch`, `git diff --stat`, and `git log -1 --oneline` in `/Users/ahmeddarrazi/Documents/projects/wt-plattform`; stop if unrelated uncommitted changes are present. +- [x] T002 Confirm the implementation branch is `299-managed-environment-cutover-final-seal` or an isolated session branch derived from it. +- [x] T003 Confirm Spec 298 changes are already committed/merged into the implementation base or intentionally isolated in a separate clean worktree/session branch before any runtime edit. +- [x] T004 Review `/Users/ahmeddarrazi/Documents/projects/wt-plattform/.specify/memory/constitution.md`, this spec package, and Specs 297 and 298 as context only. +- [x] T005 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan route:list | rg "admin/t|admin/tenants|workspaces/.*/environments|operations|provider-connections|required-permissions"`. +- [x] T006 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && rg "TenantPanelProvider|panel:\s*'tenant'|panel:\s*\"tenant\"|/admin/t/|/admin/tenants|filament\.admin\.resources\.tenants|TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext" app resources routes --glob '!vendor' --glob '!node_modules'`. +- [x] T007 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && rg "TenantResource::getUrl|TenantDashboard::getUrl|TenantRequiredPermissions::getUrl|setTenantPanelContext|/admin/t/|/admin/tenants" tests --glob '!vendor' --glob '!node_modules'`. +- [x] T008 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && rg "tenant scope|Microsoft tenant|Entra tenant|Accessible tenants|No accessible tenants|affected tenants" app resources lang tests --glob '!vendor' --glob '!node_modules'`. +- [x] T009 Update `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/299-managed-environment-cutover-final-seal/final-cutover-audit.md` with baseline findings before editing application code. +- [x] T010 Confirm the scope boundary remains explicit: no DB/model rename, no migration rewrite, no compatibility route or helper alias, no provider architecture rewrite, and no raw full-suite repair loop. + +## Phase 2: Runtime Final Seal + +**Goal**: Keep the runtime tree clean and fix only live cutover seams. + +- [x] T011 [P] Inspect `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/ManagedEnvironmentLinks.php` and confirm it remains the canonical environment-link owner. +- [x] T012 [P] Inspect `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/Workspaces/WorkspaceIntendedUrl.php` and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/Workspaces/WorkspaceRedirectResolver.php` for retired tenant-path rejection and workspace-operations normalization. +- [x] T013 [P] Inspect `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Providers/Filament/AdminPanelProvider.php` and affected `apps/platform/app/Filament/**` navigation owners for route-scope-first navigation behavior. +- [x] T014 Confirm `TenantPanelProvider` is absent from the runtime app tree and not registered in `apps/platform/bootstrap/providers.php`. +- [x] T015 Confirm any remaining `TenantResource`, `TenantDashboard`, or `TenantRequiredPermissions` runtime usage is technical-only or already routed through canonical owners. +- [x] T016 If a direct runtime legacy route/helper seam still exists, replace it with the current canonical owner and do not add a compatibility path. +- [x] T017 Re-run the focused runtime source scan from T006 and update `final-cutover-audit.md`. + +## Phase 3: Navigation Seal + +**Goal**: Keep workspace surfaces workspace-scoped and environment surfaces environment-scoped. + +- [x] T018 [P] Inspect `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Feature/Filament/PanelNavigationSegregationTest.php` as the current proof owner. +- [x] T019 Reproduce the workspace overview navigation state with a remembered environment and stale `Filament::getTenant()` context. +- [x] T020 Reproduce the canonical environment route navigation state and confirm environment-owned navigation reappears there. +- [x] T021 Keep workspace surfaces limited to workspace-owned navigation: Overview, Operations, Alerts, Audit Log, Governance inbox, Customer reviews, Manage workspaces, Integrations, and Settings where authorized. +- [x] T022 Keep environment-owned entries off workspace surfaces and visible again only on canonical environment routes. +- [x] T023 Update route-scope helpers or `shouldRegisterNavigation()` owners only if the current proof surfaces a live leak. +- [x] T024 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/PanelNavigationSegregationTest.php tests/Feature/Filament/WorkspaceOverviewNavigationTest.php tests/Feature/Filament/WorkspaceOverviewArrivalContextTest.php tests/Feature/Filament/WorkspaceContextTopbarAndTenantSelectionTest.php`. + +## Phase 4: Intended URL And Helper Retirement Proof + +**Goal**: Prevent stale session context or helper aliases from reviving retired tenant behavior. + +- [x] T025 Confirm `WorkspaceIntendedUrl` rejects `/admin/t...` and `/admin/tenants...` inputs. +- [x] T026 Confirm `WorkspaceRedirectResolver` rejects retired tenant paths and only normalizes `/admin/operations` to the canonical workspace operations route. +- [x] T027 Confirm `setAdminEnvironmentContext()` remains the active test helper in `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/tests/Pest.php`. +- [x] T028 Ensure no callable compatibility alias named `setTenantPanelContext()` is introduced. +- [x] T029 Allow `setTenantPanelContext` only as an explicit forbidden-pattern literal inside guard tests. +- [x] T030 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php tests/Feature/Guards/NoLegacyTenantPanelRuntimeTest.php`. + +## Phase 5: Product Copy Boundary And Allowlist + +**Goal**: Remove tenant-first wording from active product surfaces and classify everything else. + +- [x] T031 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` for `Accessible tenants` and `No accessible tenants in this workspace`. +- [x] T032 [P] Audit `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/resources/views/filament/pages/workspace-overview.blade.php` for `Governance risk counts affected tenants`. +- [x] T033 [P] Audit provider-facing or support/raw wording in `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Services/Providers/PlatformProviderIdentityResolver.php`, `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityEvaluator.php`, `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/Providers/ProviderReasonTranslator.php`, `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php`, `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`, and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php`. +- [x] T034 Replace active product-facing tenant-first wording with environment-first wording where the subject is a managed environment. +- [x] T035 Preserve provider-specific Microsoft/Entra tenant wording only when the external provider is the subject. +- [x] T036 Classify remaining `Tenant` references in `final-cutover-audit.md` as `fixed`, `allowed-provider-term`, `allowed-internal-model`, `allowed-historical`, `allowed-regression-guard`, `needs-follow-up`, or `blocked-runtime-finding`. +- [x] T037 Update focused copy/guard/browser assertions if touched visible labels or selectors change. + +## Phase 6: Proof Pack And Formatting + +**Goal**: Prove the final cutover seal in the narrowest honest lanes. + +- [x] T038 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/NoLegacyTenantPanelRuntimeTest.php tests/Feature/Guards/ManagedEnvironmentCanonicalRouteContractTest.php tests/Feature/Guards/NoActiveTenantResourceRoutesTest.php tests/Feature/Filament/PanelNavigationSegregationTest.php tests/Feature/Workspaces/WorkspaceIntendedUrlLegacyRejectionTest.php`. +- [x] T039 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards`. +- [x] T040 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces`. +- [x] T041 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections`. +- [x] T042 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions`. +- [x] T043 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament`. +- [x] T044 If visible browser anchors changed, run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php tests/Browser/Spec192RecordPageHeaderDisciplineSmokeTest.php`. Existing browser anchors/selectors did not change; Spec 299 instead added and ran `tests/Browser/Spec299WorkspaceOverviewCutoverSealSmokeTest.php` for the touched workspace-overview copy. +- [x] T045 Re-run the final route/source/test/copy scans from Phase 1 and update `final-cutover-audit.md` with final results. +- [x] T046 Run `cd /Users/ahmeddarrazi/Documents/projects/wt-plattform/apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`. +- [x] T047 Run `git diff --check` from `/Users/ahmeddarrazi/Documents/projects/wt-plattform`. + +## Phase 7: Close-Out Summary + +**Goal**: Finish with one reviewer-ready final seal decision. + +- [x] T048 Confirm the Filament v5 output contract in the final implementation summary: Livewire v4.0+ compliance, provider registration in `bootstrap/providers.php`, global-search handling, destructive action confirmation/authorization, asset strategy, and testing plan. +- [x] T049 Record commands run and results in the final implementation summary. +- [x] T050 Record final runtime findings, final allowlisted references, and any residual risk from `final-cutover-audit.md`. +- [x] T051 Choose one final decision string: `merge-ready; managed environment cutover sealed`, `merge-ready with documented allowed internal Tenant references`, `blocked by active legacy runtime finding`, or `blocked by navigation context leak`. + +## Dependencies & Execution Order + +- Phase 1 blocks all runtime edits. +- Phase 2, Phase 3, and Phase 4 may proceed in parallel after Phase 1 if file ownership stays clear. +- Phase 5 depends on the baseline classification from Phase 1 and can overlap with Phase 2/3 only if copy and route ownership do not collide. +- Phase 6 must run after all runtime/copy updates. +- Phase 7 closes the proof loop. + +## Parallel Execution Examples + +- T011, T012, and T013 can run in parallel because they inspect different canonical owners. +- T031, T032, and T033 can run in parallel because they audit separate copy families. +- T039, T040, and T041 can run in parallel in CI if the lane runner supports it. + +## Explicit Follow-Ups / Out Of Scope + +- Database/model/table rename from `Tenant` to `ManagedEnvironment` +- Broad historical-doc rewrite or archived-spec cleanup +- Provider architecture rewrite +- New RBAC model or new product navigation framework +- Full-suite determinism work unrelated to cutover sealing +- Reactivation of `/admin/t...`, `/admin/tenants...`, `TenantPanelProvider`, or `setTenantPanelContext()`