# Implementation Plan: Cutover Prerequisite Completion

**Branch**: `287-cutover-prerequisite-completion` | **Date**: 2026-05-10 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `specs/287-cutover-prerequisite-completion/spec.md`

## Summary

Complete the remaining runtime and test-harness seams that still block the later quality-gates / no-legacy package. The narrow implementation path retires the legacy provider-connection route family, finishes provider target-scope core neutralization on the shared provider seams, cleans environment-scope role persistence so workspace membership remains the only role-bearing truth, replaces tenant-panel-era test helpers with post-cutover admin or workspace helpers, and validates only those seams with targeted feature and browser coverage.

This plan is intentionally not a no-legacy guard package. Filament remains v5 on Livewire v4, provider registration remains in `apps/platform/bootstrap/providers.php`, no new asset or deployment step is introduced, no full-suite baseline is required, and Spec `288` remains the explicit follow-up for quality gates and no-legacy enforcement.

## Inherited Baseline / Explicit Delta

### Inherited baseline

- Spec `279` already owns the managed-environment core cutover and remains historical baseline context only.
- Spec `280` already owns the workspace-first route and panel-shell convergence, but repo truth still shows a surviving legacy provider-connection route family in `apps/platform/routes/web.php`.
- Spec `281` already owns provider boundary groundwork, but repo truth still shows Microsoft-shaped shared target-scope and identity fields on platform-core provider seams.
- Spec `282` already owns governance-artifact retargeting and remains adjacent historical context only.
- Spec `285` already owns the workspace-first RBAC direction, but repo truth still shows incomplete environment-scope persistence cleanup where managed-environment membership records mirror workspace role values.
- Spec `286` already owns UI copy cleanup and remains explicitly out of scope for this package.

### Explicit delta in this plan

- Retire the remaining legacy provider-connection route family instead of guarding it.
- Finish provider target-scope and identity neutralization on shared provider-core seams instead of leaving that work to a later enforcement slice.
- Complete the runtime cleanup that keeps workspace membership role-bearing and environment scope narrowing-only.
- Replace tenant-panel-era shared test helpers and the proof-command consumer tests `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php` and `apps/platform/tests/Feature/Rbac/TriageReviewStateAuthorizationTest.php` with post-cutover admin or workspace context helpers.
- Keep proof targeted to changed seams only and hand global enforcement to Spec `288`.

## Technical Context

**Language/Version**: PHP 8.4.15, Laravel 12.52  
**Primary Dependencies**: Pest 4, Filament 5.2.1, Livewire 4.1.4, existing provider target-scope and access-scope services, shared Pest test helpers  
**Storage**: no new persistence; the package completes behavior on existing route, provider-core, access-scope, and test-support seams  
**Testing**: targeted Pest feature tests plus targeted browser validation  
**Validation Lanes**: fast-feedback, confidence, browser  
**Target Platform**: Laravel monolith in `apps/platform`  
**Project Type**: web application  
**Performance Goals**: keep validation limited to the changed seams; no full-suite or broad guard lane work  
**Constraints**: no global guard suite, no full-suite baseline, no package execution, no guided operations, no UI copy cleanup, no provider capability expansion  
**Scale/Scope**: one bounded prerequisite-completion slice immediately preceding Spec `288`

## Likely Affected Repo Surfaces

- `apps/platform/routes/web.php`
- `apps/platform/app/Services/Providers/ProviderConnectionResolver.php`
- `apps/platform/app/Services/Providers/ProviderIdentityResolver.php`
- `apps/platform/app/Services/Providers/ProviderIdentityResolution.php`
- `apps/platform/app/Services/Providers/PlatformProviderIdentityResolver.php`
- `apps/platform/app/Services/Providers/ProviderOperationStartGate.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeNormalizer.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeDescriptor.php`
- `apps/platform/app/Services/Auth/TenantMembershipManager.php`
- `apps/platform/app/Services/Auth/ManagedEnvironmentAccessScopeResolver.php`
- `apps/platform/app/Providers/Filament/AdminPanelProvider.php`
- `apps/platform/app/Filament/Resources/TenantResource.php`
- `apps/platform/app/Filament/Pages/TenantRequiredPermissions.php`
- `apps/platform/app/Support/OperationRunLinks.php`
- `apps/platform/app/Support/Providers/ProviderReasonTranslator.php`
- `apps/platform/app/Support/Verification/VerificationLinkBehavior.php`
- `apps/platform/tests/Pest.php`
- targeted feature tests in `apps/platform/tests/Feature/ProviderConnections/`, `apps/platform/tests/Feature/Auth/`, `apps/platform/tests/Feature/Rbac/`, `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php`, and `apps/platform/tests/Feature/Rbac/TriageReviewStateAuthorizationTest.php`
- targeted browser tests in `apps/platform/tests/Browser/`

## Filament v5 / Surface Notes

- **Livewire v4.0+ compliance**: all touched surfaces remain on Filament v5 with Livewire v4.
- **Provider registration location**: provider registration remains in `apps/platform/bootstrap/providers.php`; this package does not add a new panel or change provider registration location.
- **Global search rule**: this slice introduces no new globally-searchable resource. Existing provider-connection resource behavior remains unchanged from a global-search standpoint.
- **Destructive actions**: no new destructive action is introduced. Any touched existing destructive actions must keep `->action(...)`, `->requiresConfirmation()`, and server authorization.
- **Asset strategy**: no new asset registration or deployment step is planned.

## Prerequisite Completion Fit

- Complete runtime truth first, then let Spec `288` enforce that truth.
- Prefer canonical replacement over compatibility routes, copied role persistence, or tenant-panel test-only fallbacks.
- Keep Microsoft-specific detail nested under provider-owned seams instead of the shared provider-core contract.
- Keep the package bounded to five named prerequisite areas and reject adjacent feature work.
- Use exact targeted validation commands across `spec.md`, `plan.md`, `tasks.md`, and `quickstart.md`.

## UI / Surface Guardrail Plan

- **Guardrail scope**: bounded runtime completion over existing provider-connection and provider-backed operator surfaces
- **Native vs custom classification summary**: existing native Filament provider-connection surfaces and shared provider summaries only; no new operator-facing page family
- **Shared-family relevance**: navigation, provider target-scope summaries, run-launch context, and test harness setup
- **State layers in scope**: route, page, detail, shared provider-core payloads, access-scope persistence, and test-support context helpers
- **Audience modes in scope**: operator-MSP, support-platform on existing provider surfaces only
- **Decision/diagnostic/raw hierarchy plan**: keep current provider-owned diagnostics nested; change only the shared contract and canonical route truth
- **Raw/support gating plan**: preserve existing provider-owned raw detail where current support or consent workflows genuinely need it
- **One-primary-action / duplicate-truth control**: no new action family is introduced; existing launch and open actions continue to own the workflow
- **Handling modes by drift class or surface**: implementation-required for the named seams; out of scope for global enforcement and copy cleanup
- **Repository-signal treatment**: direct runtime completion only; no broad source-scan or lint layer
- **Special surface test profiles**: standard-native-filament, global-context-shell
- **Required tests or manual smoke**: functional-core, targeted browser-smoke
- **Exception path and spread control**: provider-owned Microsoft detail only; no other exception expansion
- **Active feature PR close-out entry**: RuntimePrerequisite

## Shared Pattern & System Fit

- **Cross-cutting feature marker**: yes
- **Systems touched**: provider route ownership, shared provider target-scope and identity resolution, workspace-first access persistence cleanup, and shared test helpers
- **Shared abstractions reused**: existing provider target-scope and identity helpers, existing workspace access resolver seams, and existing `tests/Pest.php` helper style
- **New abstraction introduced? why?**: none
- **Why the existing abstraction was sufficient or insufficient**: the abstractions already exist; the incomplete cutover behavior inside them is the real remaining work
- **Bounded deviation / spread control**: Microsoft-specific profile detail remains nested only where provider-owned workflows still need it

## OperationRun UX Impact

- **Touches OperationRun start/completion/link UX?**: yes, as shared provider-backed run context and canonical link truth only
- **Central contract reused**: `ProviderOperationStartGate`, existing OperationRun context, and canonical operation links
- **Delegated UX behaviors**: existing queued, blocked, and run-link semantics remain delegated; this slice changes only the prerequisite data and route truth they rely on
- **Surface-owned behavior kept local**: existing provider launch or open actions only
- **Queued DB-notification policy**: `N/A`
- **Terminal notification path**: existing central lifecycle mechanism
- **Exception path**: none

## Provider Boundary & Portability Fit

- **Shared provider/platform boundary touched?**: yes
- **Provider-owned seams**: consent/profile detail, Microsoft-specific identifiers, Graph-specific diagnostics, and provider-owned support metadata
- **Platform-core seams**: provider-connection route ownership, shared target-scope and identity outputs, provider-backed run context, and workspace-scoped launch truth
- **Neutral platform terms / contracts preserved**: `provider connection`, `target scope`, `scope kind`, `scope identifier`, `scope display name`, `workspace`, `managed environment`
- **Retained provider-specific semantics and why**: existing consent and support flows still need Microsoft tenant/profile data, but only as provider-owned nested detail
- **Bounded extraction or follow-up path**: Spec `288` for no-legacy enforcement after this slice lands

## Constitution Check

*GATE: Must pass before implementation begins and again after design artifacts are complete.*

- Inventory-first: PASS. No new inventory or snapshot truth is introduced.
- Read/write separation: PASS. The slice completes existing runtime seams without adding a new workflow surface.
- Graph contract path: PASS by preservation. No new Graph integration family or registry is introduced.
- Deterministic capabilities: PASS by preservation. Capability families do not expand.
- RBAC-UX: PASS. Workspace membership remains role-bearing and environment scope becomes narrowing-only on the completed seams.
- Workspace isolation: PASS. The package preserves workspace-first routing and entitlement boundaries.
- Managed-environment isolation: PASS. The package narrows environment scope instead of broadening it.
- Run observability: PASS. Existing OperationRun lifecycle and links remain central.
- OperationRun start UX: PASS. No new start or completion UX family is added.
- Data minimization: PASS. No new persistence or compatibility ledger is introduced.
- Test governance: PASS. Validation stays targeted and explicit.
- Proportionality / no premature abstraction: PASS. Existing seams are completed rather than wrapped in new frameworks.
- Persisted truth / behavioral state: PASS. No new persistence or state family is introduced.
- Provider boundary: PASS. Shared provider-core contracts become more neutral while provider-owned detail stays bounded.

**Gate evaluation**: PASS.

**Post-design re-check**: PASS while `spec.md`, `plan.md`, `tasks.md`, and `quickstart.md` keep the same literal proof commands, and `research.md`, `data-model.md`, `contracts/cutover-prerequisite-completion.logical.openapi.yaml`, and `checklists/requirements.md` keep the same seam inventory and Spec `288` follow-up boundary.

## Test Governance Check

- **Test purpose / classification by changed surface**: Feature, Browser
- **Affected validation lanes**: fast-feedback, confidence, browser
- **Why this lane mix is the narrowest sufficient proof**: the changed seams are concrete runtime and test-support paths, so focused feature tests plus a narrow browser smoke are sufficient and honest
- **Narrowest proving command(s)**:
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/LegacyRedirectTest.php tests/Feature/ProviderConnections/TenantlessListRouteTest.php tests/Feature/ProviderConnections/TenantlessListScopingTest.php tests/Feature/Auth/WorkspaceFirstManagedEnvironmentAccessTest.php tests/Feature/Rbac/ProviderConnectionWorkspaceFirstPolicyTest.php tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php tests/Feature/Rbac/TriageReviewStateAuthorizationTest.php)`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php)`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail bin pint --dirty --format agent)`
- **Fixture / helper / factory / seed / context cost risks**: moderate only because the shared tenant-panel helper is being replaced on `tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php` and `tests/Feature/Rbac/TriageReviewStateAuthorizationTest.php`
- **Expensive defaults or shared helper growth introduced?**: no; the helper cutover must reduce dependence on retired panel state rather than add new implicit defaults
- **Heavy-family additions, promotions, or visibility changes**: none
- **Surface-class relief / special coverage rule**: `standard-native-filament` and `global-context-shell` remain sufficient; no heavy-governance coverage is justified
- **Closing validation and reviewer handoff**: rerun the exact commands above, verify Filament stays on Livewire v4, provider registration remains in `apps/platform/bootstrap/providers.php`, no asset registration or deployment-step drift was added, no full-suite or guard-family work was added, and confirm Spec `288` remains the explicit follow-up
- **Budget / baseline / trend follow-up**: contained feature-local increase only
- **Review-stop questions**: did the implementation widen into no-legacy guards, UI copy cleanup, package execution, guided operations, or provider capability expansion
- **Escalation path**: `document-in-feature` for contained seam follow-up, `reject-or-split` for scope expansion
- **Active feature PR close-out entry**: RuntimePrerequisite

## Review Checklist Status

- **Review checklist artifact**: `checklists/requirements.md`
- **Review outcome class**: `acceptable-special-case`
- **Workflow outcome**: `keep`
- **Test-governance outcome**: `keep`
- **Resolution note**: the package is implementation-ready as a bounded prerequisite-completion slice and no longer depends on a blocked-by-prerequisites posture
- **Escalation rule**: if implementation starts adding guard suites, full-suite baselines, or adjacent product features, stop and split the work out of `287`

## Rollout Considerations

- Retire the legacy provider-connection route family before widening targeted validation so the canonical route truth settles first.
- Complete provider target-scope neutralization before touching the browser proof so the live provider surfaces already speak the intended shared contract.
- Cut over the shared test helper before updating `apps/platform/tests/Feature/Reviews/CustomerReviewWorkspaceLaunchLinksTest.php` and `apps/platform/tests/Feature/Rbac/TriageReviewStateAuthorizationTest.php` so targeted tests do not drift through two helper systems.
- Keep Spec `288` untouched during runtime completion; it should start from the completed baseline produced by this slice.

## Risk Controls

- Reject any implementation that keeps legacy provider-connection aliases as compatibility routes for convenience.
- Reject any implementation that solves provider target-scope cleanup by hiding Microsoft-only fields in new platform-core wrappers instead of neutralizing the shared contract.
- Reject any implementation that preserves copied role-bearing state on environment-scope persistence after the cleanup.
- Reject any implementation that adds a global guard family, broad source-scan package, or full-suite baseline under this spec.

## Research & Design Outputs

- `research.md` records the completion-first decisions, bounded runtime scope, rejected guard-suite alternative, and evidence anchors.
- `data-model.md` captures the derived seam inventory, canonical replacements, and invariants.
- `quickstart.md` gives reviewers the scope boundary, review scenarios, and exact targeted proof commands.
- `contracts/cutover-prerequisite-completion.logical.openapi.yaml` models the logical completion seams and the targeted validation contract.
- `checklists/requirements.md` records the review outcome and bounded scope rules.

## Project Structure

### Documentation (this feature)

```text
specs/287-cutover-prerequisite-completion/
├── checklists/
│   └── requirements.md
├── contracts/
│   └── cutover-prerequisite-completion.logical.openapi.yaml
├── data-model.md
├── plan.md
├── quickstart.md
├── research.md
├── spec.md
└── tasks.md
```

### Source Code (repository root)

```text
apps/platform/
├── app/
├── routes/
└── tests/
    ├── Browser/
    ├── Feature/
    └── Pest.php
```

**Structure Decision**: keep the package inside the existing Laravel app, route, provider, access-service, and Pest test-support structure. No new base directory is needed.